20 static constexpr
bool value =
false;
24 template <
typename T,
int... n>
26 static constexpr
bool value =
true;
37 template <
typename S,
typename T,
int m,
int... n,
41 tensor<decltype(S{} * T{}), m, n...> C{};
42 for (
int i = 0; i < m; i++) {
56 template <
typename S,
typename T,
int m,
int... n,
57 typename = std::enable_if_t<std::is_arithmetic_v<S> || is_dual_number<S>::value>>
60 tensor<decltype(T{} * S{}), m, n...> C{};
61 for (
int i = 0; i < m; i++) {
75 template <
typename S,
typename T,
int m,
int... n,
76 typename = std::enable_if_t<std::is_arithmetic_v<S> || is_dual_number<S>::value>>
79 tensor<decltype(S{} * T{}), n...> C{};
80 for (
int i = 0; i < m; i++) {
94 template <
typename S,
typename T,
int m,
int... n,
95 typename = std::enable_if_t<std::is_arithmetic_v<S> || is_dual_number<S>::value>>
98 tensor<decltype(T{} * S{}), m, n...> C{};
99 for (
int i = 0; i < m; i++) {
106 template <
int i,
typename S,
typename T>
107 struct one_hot_helper;
109 template <
int i,
int... I,
typename T>
110 struct one_hot_helper<i, std::integer_sequence<int, I...>, T> {
111 using type = tuple<std::conditional_t<i == I, T, zero>...>;
114 template <
int i,
int n,
typename T>
115 struct one_hot :
public one_hot_helper<i, std::make_integer_sequence<int, n>, T> {};
124 template <
int i,
int n,
typename T>
128 template <
int i,
int N>
141 template <
int i,
int N>
146 arg_dual.
value = arg;
147 smith::get<i>(arg_dual.gradient) = 1.0;
158 template <
int i,
int N,
typename T,
int... n>
164 arg_dual(j...).value = arg(j...);
165 smith::get<i>(arg_dual(j...).gradient)(j...) = 1.0;
187 template <
typename T0,
typename T1>
190 return tuple{make_dual_helper<0, 2>(get<0>(args)), make_dual_helper<1, 2>(get<1>(args))};
194 template <
typename T0,
typename T1,
typename T2>
197 return tuple{make_dual_helper<0, 3>(get<0>(args)), make_dual_helper<1, 3>(get<1>(args)),
198 make_dual_helper<2, 3>(get<2>(args))};
208 template <
bool dualify,
typename T>
211 if constexpr (dualify) {
214 if constexpr (!dualify) {
227 template <
bool dualify,
typename T,
int n>
230 if constexpr (dualify) {
231 using return_type = decltype(
make_dual(T{}));
233 for (
int i = 0; i < n; i++) {
238 if constexpr (!dualify) {
244 template <
int n,
typename... T,
int... i>
263 return smith::make_tuple(promote_to_dual_when < i == n > (smith::get<i>(args))...);
273 template <
int n,
typename... T>
276 return make_dual_helper<n>(args, std::make_integer_sequence<
int,
static_cast<int>(
sizeof...(T))>{});
288 template <
typename T1,
typename T2,
int n>
292 for (
int i = 0; i < n; i++) {
303 template <
typename... T>
314 template <
typename... T>
321 template <
typename... T,
int... n>
326 for_constexpr<
sizeof...(T)>([&](
auto j) { smith::get<j>(g)(i...) = smith::get<j>(arg(i...).gradient); });
332 template <
typename... T>
348 A_dual(i...).value = A(i...);
349 A_dual(i...).gradient(i...) = 1.0;
363 template <
typename T,
int n>
366 constexpr
auto abs = [](
double x) {
return (x < 0) ? -x : x; };
367 constexpr
auto swap = [](
auto& x,
auto& y) {
378 for (
int i = 0; i < n; i++) {
387 for (
int i = 0; i < n; i++) {
392 for (
int j = i + 1; j < n; j++) {
394 if (
abs(U_ji) > max_val) {
400 swap(P[max_row], P[i]);
401 swap(U[max_row], U[i]);
404 for (
int i = 0; i < n; i++) {
407 for (
int j = i + 1; j < n; j++) {
408 auto c = U[j][i] / U[i][i];
424 template <
typename S,
typename T,
int n,
int... m>
444 if constexpr (
is_zero<decltype(dx)>{}) {
454 template <
typename T,
int n>
457 return make_tensor<n>([&](
int i) {
return dual<T>{x[i], dx[i]}; });
463 template <
typename T,
int m,
int n>
466 return make_tensor<m, n>([&](
int i,
int j) {
return dual<T>{x[i][j], dx[i][j]}; });
478 template <
typename gradient_type,
int n>
482 return make_tensor<n, n>([&](
int i,
int j) {
483 auto value = invA[i][j];
484 gradient_type gradient{};
485 for (
int k = 0; k < n; k++) {
486 for (
int l = 0; l < n; l++) {
487 gradient -= invA[i][k] * A[k][l].gradient * invA[l][j];
498 template <
typename T,
int... n>
501 tensor<double, n...> value{};
502 for_constexpr<n...>([&](
auto... i) { value(i...) = arg(i...).value; });
514 for_constexpr<n...>([&](
auto... i) { g(i...) = arg(i...).gradient; });
519 template <
int... n,
int... m>
522 tensor<double, n..., m...> g{};
523 for_constexpr<n...>([&](
auto... i) { g(i...) = arg(i...).gradient; });
575 template <
typename function,
typename... ParamTypes>
580 double fl = f(lower_bound,
get_value(params)...);
581 double fh = f(upper_bound,
get_value(params)...);
583 SLIC_ERROR_ROOT_IF(fl * fh > 0,
"solve_scalar_equation: root not bracketed by input bounds.");
585 unsigned int iterations = 0;
586 bool converged =
false;
592 }
else if (fh == 0) {
602 double xl = lower_bound;
603 double xh = upper_bound;
610 if (x0 < lower_bound || x0 > upper_bound) {
611 x0 = 0.5 * (lower_bound + upper_bound);
615 double delta_x_old =
std::abs(upper_bound - lower_bound);
616 double delta_x = delta_x_old;
622 if (iterations == options.
max_iter) {
623 SLIC_WARNING(
"solve_scalar_equation failed to converge in allotted iterations.");
628 if ((x - xh) * df_dx - fval > 0 || (x - xl) * df_dx - fval < 0 ||
630 delta_x_old = delta_x;
631 delta_x = 0.5 * (xh - xl);
633 converged = (x == xl);
635 delta_x_old = delta_x;
636 delta_x = fval / df_dx;
639 converged = (x == temp);
669 constexpr
bool contains_duals =
671 if constexpr (contains_duals) {
672 auto [fval, df] = f(x, params...);
673 auto dx = -df / df_dx;
677 if constexpr (!contains_duals) {
678 auto fval = f(x, params...);
680 return tuple{x, status};
696 template <
typename function,
int n>
700 "error: f(x) must have the same number of equations as unknowns");
702 double epsilon = 1.0e-8;
703 int max_iterations = 10;
707 for (
int k = 0; k < max_iterations; k++) {
710 if (
norm(r) < epsilon)
break;
726 template <
typename T,
int size>
731 for (
int i = 0; i <
size; i++) {
732 for (
int j = 0; j <
size; j++) {
734 matA(i, j) = A[i][j].value;
736 matA(i, j) = A[i][j];
742 mfem::DenseMatrixEigensystem eig_sys(matA);
747 for (
int k = 0; k <
size; k++) {
749 output[k] = eig_sys.Eigenvalue(k);
754 auto dA = make_tensor<size, size>([&](
int i,
int j) {
return A(i, j).gradient; });
755 output[k].gradient =
dot(phi, dA, phi);
768 template <
typename T>
773 return (T(0) < val) - (val < T(0));
782 template <
typename T>
785 auto swap = [](
int& first,
int& second) {
791 if (v[0] > v[1]) swap(order[0], order[1]);
792 if (v[order[1]] > v[order[2]]) swap(order[1], order[2]);
793 if (v[order[0]] > v[order[1]]) swap(order[0], order[1]);
816 double J2 = 0.5 *
inner(A_dev, A_dev);
817 double J3 =
det(A_dev);
821 double tmp = (0.5 * J3) *
std::pow(3.0 / J2, 1.5);
822 double alpha =
std::acos(fmin(fmax(tmp, -1.0), 1.0)) / 3.0;
825 if (6.0 * alpha < M_PI) {
835 double norm_max = -1.0;
837 for (
int i = 0; i < 3; i++) {
838 for (
int j = 0; j < 3; j++) {
839 r[i][j] = A_dev(j, i) - (i == j) * eta(0);
842 double norm_r =
norm(r[i]);
843 if (norm_max < norm_r) {
849 vec3 s0, s1, t1, t2, v0, v1, v2, w;
852 t1 = r[(imax + 1) % 3] -
dot(r[(imax + 1) % 3], s0) * s0;
853 t2 = r[(imax + 2) % 3] -
dot(r[(imax + 2) % 3], s0) * s0;
858 for (
int i = 0; i < 3; i++) {
864 auto A_dev_s0 =
dot(A_dev, s0);
865 auto A_dev_s1 =
dot(A_dev, s1);
867 double A11 =
dot(s0, A_dev_s0);
868 double A12 =
dot(s0, A_dev_s1);
870 double A22 =
dot(s1, A_dev_s1);
872 double delta = 0.5 *
std::sqrt((A11 - A22) * (A11 - A22) + 4 * A12 * A21);
874 eta(1) = 0.5 * (A11 + A22) - delta;
875 eta(2) = 0.5 * (A11 + A22) + delta;
880 if (fabs(delta) <= 1.0e-15) {
881 for (
int i = 0; i < 3; i++) {
888 t1 = A_dev_s0 - eta(1) * s0;
889 t2 = A_dev_s1 - eta(1) * s1;
894 for (
int i = 0; i < 3; i++) Q[i][1] = v1(i);
900 for (
int i = 0; i < 3; i++) Q[i][2] = v2(i);
905 for (
int i = 0; i < 3; i++) eta[i] +=
tr(A) / 3.0;
909 vec3 eigvals{{eta[order[0]], eta[order[1]], eta[order[2]]}};
911 mat3 eigvecs{{{Q[0][order[0]], Q[0][order[1]], Q[0][order[2]]},
912 {Q[1][order[0]], Q[1][order[1]], Q[1][order[2]]},
913 {Q[2][order[0]], Q[2][order[1]], Q[2][order[2]]}}};
916 return {eigvals, eigvecs};
967 template <
typename T,
typename Function,
typename EigvalSecantFunction>
972 for (
int i = 0; i < 3; i++) {
985 template <
typename Gradient,
typename Function>
990 return make_tensor<3, 3>([&](
int i,
int j) {
991 auto value = f_A[i][j];
993 for (
int k = 0; k < 3; k++) {
994 for (
int l = 0; l < 3; l++) {
995 for (
int a = 0; a < 3; a++) {
996 for (
int b = 0; b < 3; b++) {
997 gradient += g(lambda[a], lambda[b]) * Q[k][a] * Q[l][b] * Q[i][a] * Q[j][b] * A[k][l].gradient;
1012 template <
typename T>
1015 auto g = [](
double lam1,
double lam2) {
1019 double y = lam1 / lam2;
1020 return (
std::log(y) / (y - 1.0)) / lam2;
1032 template <
typename T>
1035 auto g = [](
double lam1,
double lam2) {
1039 double arg = lam1 - lam2;
1040 return std::exp(lam2) * std::expm1(arg) / arg;
1052 template <
typename T>
1055 auto g = [](
double lam1,
double lam2) {
return 1.0 / (
std::sqrt(lam1) +
std::sqrt(lam2)); };
#define SMITH_HOST_DEVICE
Macro that evaluates to __host__ __device__ when compiling with nvcc and does nothing on a host compi...
This file contains the declaration of a dual number class.
Accelerator functionality.
SMITH_HOST_DEVICE auto log(dual< gradient_type > a)
implementation of the natural logarithm function for dual numbers
const ScalarSolverOptions default_solver_options
Default options for solve_scalar_equation.
SMITH_HOST_DEVICE auto promote_to_dual_when(const T &x)
a function that optionally (decided at compile time) converts a value to its dual type
SMITH_HOST_DEVICE auto acos(dual< gradient_type > a)
implementation of acos for dual numbers
SMITH_HOST_DEVICE auto exp(dual< gradient_type > a)
implementation of exponential function for dual numbers
SMITH_HOST_DEVICE auto apply(lambda f, tuple< T... > &args)
a way of passing an n-tuple to a function that expects n separate arguments
auto solve_scalar_equation(const function &f, double x0, double lower_bound, double upper_bound, ScalarSolverOptions options, ParamTypes... params)
Solves a nonlinear scalar-valued equation and gives derivatives of solution to parameters.
auto eigenvalues(const smith::tensor< T, size, size > &A)
compute the eigenvalues of a symmetric matrix A
constexpr SMITH_HOST_DEVICE auto operator*(const dual< gradient_type > &a, double b)
multiplication of a dual number and a non-dual number
constexpr SMITH_HOST_DEVICE auto inv(const isotropic_tensor< T, m, m > &I)
return the inverse of an isotropic tensor
auto log_symm(tensor< T, 3, 3 > A)
Logarithm of a symmetric matrix.
SMITH_HOST_DEVICE auto sqrt(dual< gradient_type > x)
implementation of square root for dual numbers
constexpr SMITH_HOST_DEVICE auto norm(const isotropic_tensor< T, m, m > &I)
compute the Frobenius norm (sqrt(tr(dot(transpose(I), I)))) of an isotropic tensor
constexpr SMITH_HOST_DEVICE auto type(const tuple< T... > &values)
a function intended to be used for extracting the ith type from a tuple.
SMITH_HOST_DEVICE tuple< vec3, mat3 > eig_symm(const mat3 &A)
SMITH_HOST_DEVICE auto cos(dual< gradient_type > a)
implementation of cosine for dual numbers
auto sqrt_symm(tensor< T, 3, 3 > A)
Square root of a symmetric matrix.
constexpr SMITH_HOST_DEVICE auto make_dual_helper(zero)
This is an overloaded member function, provided for convenience. It differs from the above function o...
constexpr SMITH_HOST_DEVICE LuFactorization< T, n > factorize_lu(const tensor< T, n, n > &A)
Compute LU factorization of a matrix with partial pivoting.
constexpr SMITH_HOST_DEVICE auto get_gradient(dual< gradient_type > arg)
return the "gradient" part from a dual number type
constexpr SMITH_HOST_DEVICE int size(const tensor< T, n... > &)
returns the total number of stored values in a tensor
constexpr SMITH_HOST_DEVICE auto transpose(const isotropic_tensor< T, m, m > &I)
return the transpose of an isotropic tensor
constexpr SMITH_HOST_DEVICE auto symmetric_mat3_function_with_derivative(tensor< dual< Gradient >, 3, 3 > A, tensor< double, 3, 3 > f_A, vec3 lambda, mat3 Q, const Function &g)
Helper function for defining the derivative.
auto symmetric_mat3_function(tensor< T, 3, 3 > A, const Function &f, const EigvalSecantFunction &g)
Constructs an isotropic tensor-valued function of a symmetric 3x3 tensor from a scalar function.
int sgn(T val)
Signum, returns sign of input.
SMITH_HOST_DEVICE tuple< T... > make_tuple(const T &... args)
helper function for combining a list of values into a tuple
constexpr SMITH_HOST_DEVICE auto dev(const tensor< T, n, n > &A)
Calculates the deviator of a matrix (rank-2 tensor)
SMITH_HOST_DEVICE auto pow(dual< gradient_type > a, dual< gradient_type > b)
implementation of a (dual) raised to the b (dual) power
constexpr SMITH_HOST_DEVICE auto get_value(const T &arg)
return the "value" part from a given type. For non-dual types, this is just the identity function
SMITH_HOST_DEVICE auto abs(dual< gradient_type > x)
Implementation of absolute value function for dual numbers.
constexpr SMITH_HOST_DEVICE tensor< T, n, n > diag(const tensor< T, n > &d)
Returns a square diagonal matrix by specifying the diagonal entries.
auto find_root(const function &f, tensor< double, n > x0)
Finds a root of a vector-valued nonlinear function.
constexpr auto make_dual_wrt(const smith::tuple< T... > &args)
take a tuple of values, and promote the nth one to a one-hot dual number of the appropriate type
typename detail::outer_prod< T1, T2 >::type outer_product_t
a type function that returns the tensor type of an outer product of two tensors
auto exp_symm(tensor< T, 3, 3 > A)
Exponential of a symmetric matrix.
constexpr SMITH_HOST_DEVICE auto det(const isotropic_tensor< T, m, m > &I)
compute the determinant of an isotropic tensor
constexpr SMITH_HOST_DEVICE auto inner(const dual< S > &A, const dual< T > &B)
constexpr SMITH_HOST_DEVICE auto tr(const isotropic_tensor< T, m, m > &I)
calculate the trace of an isotropic tensor
auto cross(const tensor< T, 3, 2 > &A)
compute the cross product of the columns of A: A(:,1) x A(:,2)
SMITH_HOST_DEVICE auto normalize(const tensor< T, n... > &A)
Normalizes the tensor Each element is divided by the Frobenius norm of the tensor,...
constexpr SMITH_HOST_DEVICE auto dot(const isotropic_tensor< S, m, m > &I, const tensor< T, m, n... > &A)
dot product between an isotropic and (nonisotropic) tensor
SMITH_HOST_DEVICE auto promote_each_to_dual_when(const tensor< T, n > &x)
a function that optionally (decided at compile time) converts a list of values to their dual types
typename one_hot< i, n, T >::type one_hot_t
a tuple type with n entries, all of which are of type smith::zero, except for the i^{th} entry,...
SMITH_HOST_DEVICE tensor< int, 3 > argsort(const tensor< T, 3 > &v)
Find indices that would sort a 3-vector.
constexpr SMITH_HOST_DEVICE auto operator/(const dual< gradient_type > &a, double b)
division of a dual number by a non-dual number
constexpr SMITH_HOST_DEVICE auto linear_solve(const LuFactorization< S, n > &lu_factors, const tensor< T, n, m... > &b)
constexpr SMITH_HOST_DEVICE auto make_dual(double x)
promote a value to a dual number of the appropriate type
Representation of an LU factorization.
Settings for solve_scalar_equation.
double rtol
absolute tolerance on absolute value of residual
unsigned int max_iter
maximum allowed number of iterations
double xtol
absolute tolerance on Newton correction
Status and diagnostics of nonlinear equation solvers.
unsigned int iterations
Number of iterations taken.
bool converged
converged Flag indicating whether solver converged to a solution or aborted.
double residual
Final value of residual.
Dual number struct (value plus gradient)
double value
the actual numerical value
class for checking if a type is a dual number or not
class for checking if a type is a tensor of dual numbers or not
static constexpr bool value
whether or not type T is a dual number
Arbitrary-rank tensor class.
Type that mimics std::tuple.
Type that mimics std::tuple.
This is a class that mimics most of std::tuple's interface, except that it is usable in CUDA kernels ...
A sentinel struct for eliding no-op tensor operations.
Implementation of the tensor class used by Functional.
Implements a std::tuple-like object that works in CUDA kernels.