diff --git a/.travis.yml b/.travis.yml index 05480a8c..a8c2208d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ os: - osx julia: + - 0.7 - nightly branches: diff --git a/docs/make.jl b/docs/make.jl index f9afcc48..cabbfb2e 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -24,7 +24,7 @@ makedocs( deploydocs( repo = "github.com/KristofferC/Tensors.jl.git", target = "build", - julia = "nightly", # deploy from release bot + julia = "0.7", deps = nothing, make = nothing ) diff --git a/docs/src/man/constructing_tensors.md b/docs/src/man/constructing_tensors.md index 5c08b6c4..1001be00 100644 --- a/docs/src/man/constructing_tensors.md +++ b/docs/src/man/constructing_tensors.md @@ -11,6 +11,11 @@ end Tensors can be created in multiple ways but they usually include running a function on tensor types of which there are two kinds, `Tensor{order, dim, T}` for non-symmetric tensors and `SymmetricTensor{order, dim, T}` for symmetric tensors. The parameter `order` is an integer of value 1, 2 or 4, excluding 1 for symmetric tensors. The second parameter `dim` is an integer which corresponds to the dimension of the tensor and can be 1, 2 or 3. The last parameter `T` is the number type that the tensors contain, i.e. `Float64` or `Float32`. +```@docs +Tensors.Tensor +Tensors.SymmetricTensor +``` + ## [Zero tensors](@id zero_tensors) A tensor with only zeros is created using the function `zero`, applied to the type of tensor that should be created: diff --git a/docs/src/man/other_operators.md b/docs/src/man/other_operators.md index 37d4be70..a56f790f 100644 --- a/docs/src/man/other_operators.md +++ b/docs/src/man/other_operators.md @@ -152,10 +152,9 @@ $\mathbf{A} \cdot \mathbf{v}_i = \lambda_i \mathbf{v}_i \qquad i = 1, \dots, \te where ``\lambda_i`` are the eigenvalues and ``\mathbf{v}_i`` are the corresponding eigenvectors. ```@docs -Tensors.eigfact +Tensors.eigen Tensors.eigvals Tensors.eigvecs -Tensors.eig ``` ## Tensor square root diff --git a/src/Tensors.jl b/src/Tensors.jl index b96ab067..cfb5e635 100644 --- a/src/Tensors.jl +++ b/src/Tensors.jl @@ -6,7 +6,7 @@ import Base.@pure using LinearAlgebra # re-exports from LinearAlgebra -export ⋅, ×, dot, diagm, tr, det, norm, eig, eigvals, eigvecs, eigfact +export ⋅, ×, dot, diagm, tr, det, norm, eigvals, eigvecs, eigen export AbstractTensor, SymmetricTensor, Tensor, Vec, FourthOrderTensor, SecondOrderTensor @@ -22,11 +22,40 @@ export tovoigt, tovoigt!, fromvoigt, tomandel, tomandel!, frommandel ######### abstract type AbstractTensor{order, dim, T <: Real} <: AbstractArray{T, order} end +""" + SymmetricTensor{order,dim,T<:Real} + +Symmetric tensor type supported for `order ∈ (2,4)` and `dim ∈ (1,2,3)`. +`SymmetricTensor{4}` is a minor symmetric tensor, such that +`A[i,j,k,l] == A[j,i,k,l]` and `A[i,j,k,l] == A[i,j,l,k]`. + +# Examples +```jldoctest +julia> SymmetricTensor{2,2,Float64}((1.0, 2.0, 3.0)) +2×2 SymmetricTensor{2,2,Float64,3}: + 1.0 2.0 + 2.0 3.0 +``` +""" struct SymmetricTensor{order, dim, T, M} <: AbstractTensor{order, dim, T} data::NTuple{M, T} SymmetricTensor{order, dim, T, M}(data::NTuple) where {order, dim, T, M} = new{order, dim, T, M}(data) end +""" + Tensor{order,dim,T<:Real} + +Tensor type supported for `order ∈ (1,2,4)` and `dim ∈ (1,2,3)`. + +# Examples +```jldoctest +julia> Tensor{1,3,Float64}((1.0, 2.0, 3.0)) +3-element Tensor{1,3,Float64,3}: + 1.0 + 2.0 + 3.0 +``` +""" struct Tensor{order, dim, T, M} <: AbstractTensor{order, dim, T} data::NTuple{M, T} diff --git a/src/eigen.jl b/src/eigen.jl index 86c76251..8334da58 100644 --- a/src/eigen.jl +++ b/src/eigen.jl @@ -1,11 +1,11 @@ # MIT License: Copyright (c) 2016: Andy Ferris. # See LICENSE.md for further licensing test -@inline function LinearAlgebra.eigfact(S::SymmetricTensor{2, 1, T}) where {T} +@inline function LinearAlgebra.eigen(S::SymmetricTensor{2, 1, T}) where {T} @inbounds Eigen(Vec{1, T}((S[1, 1],)), one(Tensor{2, 1, T})) end -function LinearAlgebra.eigfact(S::SymmetricTensor{2, 2, T}) where {T} +function LinearAlgebra.eigen(S::SymmetricTensor{2, 2, T}) where {T} @inbounds begin # eigenvalues from quadratic formula trS_half = tr(S) / 2 @@ -36,7 +36,7 @@ end # A small part of the code in the following method was inspired by works of David # Eberly, Geometric Tools LLC, in code released under the Boost Software # License. See LICENSE.md -function LinearAlgebra.eigfact(S::SymmetricTensor{2, 3, T}) where {T} +function LinearAlgebra.eigen(S::SymmetricTensor{2, 3, T}) where {T} @inbounds begin R = typeof((one(T)*zero(T) + zero(T))/one(T)) SR = convert(SymmetricTensor{2, 3, R}, S) diff --git a/src/math_ops.jl b/src/math_ops.jl index d06fcb0a..4e5872d1 100644 --- a/src/math_ops.jl +++ b/src/math_ops.jl @@ -157,61 +157,30 @@ end Base.:\(S1::SecondOrderTensor, S2::AbstractTensor) = inv(S1) ⋅ S2 -""" - eig(::SymmetricTensor{2}) - -Compute the eigenvalues and eigenvectors of a symmetric second order tensor. -`eig` is a wrapper around [`eigfact`](@ref) which extracts eigenvalues and -eigenvectors to a tuple. - -# Examples -```jldoctest -julia> A = rand(SymmetricTensor{2, 2}) -2×2 SymmetricTensor{2,2,Float64,3}: - 0.590845 0.766797 - 0.766797 0.566237 - -julia> Λ, Φ = eig(A); - -julia> Λ -2-element Tensor{1,2,Float64,2}: - -0.1883547111127678 - 1.345436766284664 - -julia> Φ -2×2 Tensor{2,2,Float64,4}: - -0.701412 0.712756 - 0.712756 0.701412 - -julia> Φ ⋅ diagm(Tensor{2, 2}, Λ) ⋅ inv(Φ) # Same as A -2×2 Tensor{2,2,Float64,4}: - 0.590845 0.766797 - 0.766797 0.566237 -``` -""" -@inline LinearAlgebra.eig(S::SymmetricTensor) = (E = eigfact(S); (E.λ, E.Φ)) - """ eigvals(::SymmetricTensor{2}) Compute the eigenvalues of a symmetric second order tensor. """ -@inline LinearAlgebra.eigvals(S::SymmetricTensor) = (E = eigfact(S); E.λ) +@inline LinearAlgebra.eigvals(S::SymmetricTensor) = (E = eigen(S); E.values) """ eigvecs(::SymmetricTensor{2}) Compute the eigenvectors of a symmetric second order tensor. """ -@inline LinearAlgebra.eigvecs(S::SymmetricTensor) = (E = eigfact(S); E.Φ) +@inline LinearAlgebra.eigvecs(S::SymmetricTensor) = (E = eigen(S); E.vectors) struct Eigen{T, dim, M} - λ::Vec{dim, T} - Φ::Tensor{2, dim, T, M} + values::Vec{dim, T} + vectors::Tensor{2, dim, T, M} end +# destructure via iteration +Base.iterate(E::Eigen, state::Int=1) = iterate((E.values, E.vectors), state) + """ - eigfact(::SymmetricTensor{2}) + eigen(A::SymmetricTensor{2}) Compute the eigenvalues and eigenvectors of a symmetric second order tensor and return an `Eigen` object. The eigenvalues are stored in a `Vec`, @@ -222,39 +191,36 @@ See [`eigvals`](@ref) and [`eigvecs`](@ref). # Examples ```jldoctest -julia> A = rand(SymmetricTensor{2, 2}) -2×2 SymmetricTensor{2,2,Float64,3}: - 0.590845 0.766797 - 0.766797 0.566237 +julia> A = rand(SymmetricTensor{2, 2}); -julia> E = eigfact(A) +julia> E = eigen(A) Tensors.Eigen{Float64,2,4}([-0.188355, 1.34544], [-0.701412 0.712756; 0.712756 0.701412]) -julia> eigvals(E) +julia> E.values 2-element Tensor{1,2,Float64,2}: -0.1883547111127678 1.345436766284664 -julia> eigvecs(E) +julia> E.vectors 2×2 Tensor{2,2,Float64,4}: -0.701412 0.712756 0.712756 0.701412 ``` """ -LinearAlgebra.eigfact +LinearAlgebra.eigen """ eigvals(::Eigen) -Extract eigenvalues from an `Eigen` object, returned by [`eigfact`](@ref). +Extract eigenvalues from an `Eigen` object, returned by [`eigen`](@ref). """ -@inline LinearAlgebra.eigvals(E::Eigen) = E.λ +@inline LinearAlgebra.eigvals(E::Eigen) = E.values """ eigvecs(::Eigen) -Extract eigenvectors from an `Eigen` object, returned by [`eigfact`](@ref). +Extract eigenvectors from an `Eigen` object, returned by [`eigen`](@ref). """ -@inline LinearAlgebra.eigvecs(E::Eigen) = E.Φ +@inline LinearAlgebra.eigvecs(E::Eigen) = E.vectors """ sqrt(S::SymmetricTensor{2}) @@ -290,7 +256,9 @@ function Base.sqrt(S::SymmetricTensor{2,2}) end function Base.sqrt(S::SymmetricTensor{2,3,T}) where T - λ, Φ = eig(S) + E = eigen(S) + λ = E.values + Φ = E.vectors z = zero(T) Λ = Tensor{2,3}((√(λ[1]), z, z, z, √(λ[2]), z, z, z, √(λ[3]))) return symmetric(Φ⋅Λ⋅Φ') diff --git a/src/symmetric.jl b/src/symmetric.jl index 50e805c2..758c58f0 100644 --- a/src/symmetric.jl +++ b/src/symmetric.jl @@ -3,9 +3,8 @@ symmetric(::SecondOrderTensor) symmetric(::FourthOrderTensor) -Computes the symmetric part of a second or fourth order tensor. -For a fourth order tensor, the symmetric part is the same as the minor symmetric part. -Returns a `SymmetricTensor`. +Computes the (minor) symmetric part of a second or fourth order tensor. +Return a `SymmetricTensor`. # Examples ```jldoctest @@ -31,7 +30,7 @@ end """ minorsymmetric(::FourthOrderTensor) -Computes the minor symmetric part of a fourth order tensor, returns a `SymmetricTensor{4}`. +Compute the minor symmetric part of a fourth order tensor, return a `SymmetricTensor{4}`. """ @inline function minorsymmetric(S::Tensor{4, dim}) where {dim} SymmetricTensor{4, dim}( @@ -52,7 +51,7 @@ end """ majorsymmetric(::FourthOrderTensor) -Computes the major symmetric part of a fourth order tensor, returns a `Tensor{4}`. +Compute the major symmetric part of a fourth order tensor, returns a `Tensor{4}`. """ @inline function majorsymmetric(S::FourthOrderTensor{dim}) where {dim} Tensor{4, dim}( diff --git a/src/transpose.jl b/src/transpose.jl index 4a1936a5..4951f3bf 100644 --- a/src/transpose.jl +++ b/src/transpose.jl @@ -49,8 +49,4 @@ Compute the major transpose of a fourth order tensor. Tensor{4, dim}(@inline function(i, j, k, l) @inbounds S[k,l,i,j]; end) end -if VERSION >= v"0.7.0-DEV.1415" - @inline Base.adjoint(S::AllTensors) = transpose(S) -else - @inline Base.ctranspose(S::AllTensors) = transpose(S) -end +@inline Base.adjoint(S::AllTensors) = transpose(S) diff --git a/test/F64.jl b/test/F64.jl index fa255ef6..ba3954a3 100644 --- a/test/F64.jl +++ b/test/F64.jl @@ -36,7 +36,7 @@ Base.convert(::Type{F64}, a::F64) = a Base.convert(::Type{Float64}, a::F64) = a.x Base.convert(::Type{F64}, a::T) where {T <: Number} = F64(a) -# for testing of eigfact +# for testing of eigen Base.acos(a::F64) = F64(acos(a.x)) Base.cos(a::F64) = F64(cos(a.x)) Base.sin(a::F64) = F64(sin(a.x)) diff --git a/test/test_misc.jl b/test/test_misc.jl index 14ee5fd7..e461fbc4 100644 --- a/test/test_misc.jl +++ b/test/test_misc.jl @@ -294,9 +294,9 @@ for T in (Float32, Float64, F64), dim in (1,2,3) @test AA ⊡ (@inferred inv(AA))::Tensor{4, dim, T} ≈ one(Tensor{4, dim, T}) @test AA_sym ⊡ (@inferred inv(AA_sym))::SymmetricTensor{4, dim, T} ≈ one(SymmetricTensor{4, dim, T}) - E = @inferred eigfact(t_sym) - Λ, Φ = @inferred eig(t_sym) - Λa, Φa = eig(Array(t_sym)) + E = @inferred eigen(t_sym) + Λ, Φ = E + Λa, Φa = eigen(Array(t_sym)) @test Λ ≈ (@inferred eigvals(t_sym)) ≈ eigvals(E) ≈ Λa @test Φ ≈ (@inferred eigvecs(t_sym)) ≈ eigvecs(E) @@ -308,9 +308,9 @@ for T in (Float32, Float64, F64), dim in (1,2,3) # test eigenfactorizations for a diagonal tensor v = rand(T, dim) d_sym = diagm(SymmetricTensor{2, dim, T}, v) - E = @inferred eigfact(d_sym) - Λ, Φ = @inferred eig(d_sym) - Λa, Φa = eig(Symmetric(Array(d_sym))) + E = @inferred eigen(d_sym) + Λ, Φ = E + Λa, Φa = eigen(Symmetric(Array(d_sym))) @test Λ ≈ (@inferred eigvals(d_sym)) ≈ eigvals(E) ≈ Λa @test Φ ≈ (@inferred eigvecs(d_sym)) ≈ eigvecs(E)