diff --git a/.travis.yml b/.travis.yml index cf834bd8..81fc49db 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,23 +6,18 @@ os: - osx - linux julia: - - 0.6 - - nightly + - 0.7 + - 1.0 env: - CONDA_JL_VERSION="2" PYTHON="" - CONDA_JL_VERSION="3" PYTHON="" notifications: email: false script: - - julia -e 'Pkg.clone(pwd())' - - julia -e 'Pkg.build("TensorFlow")' - - julia -e 'Pkg.test("TensorFlow", coverage=true)' + - julia -e 'using Pkg; Pkg.clone(pwd())' + - julia -e 'using Pkg; Pkg.build("TensorFlow")' + - julia -e 'using Pkg; Pkg.test("TensorFlow", coverage=true)' after_success: -- julia -e 'cd(Pkg.dir("TensorFlow")); Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())' -- julia -e 'Pkg.add("Documenter")' -- julia -e 'cd(Pkg.dir("TensorFlow")); include(joinpath("docs", "make.jl"))' -matrix: - fast_finish: true - allow_failures: - - julia: nightly - - os: osx +- julia -e 'using Pkg; cd(Pkg.dir("TensorFlow")); Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())' +- julia -e 'using Pkg; Pkg.add("Documenter")' +- julia -e 'using Pkg; cd(Pkg.dir("TensorFlow")); include(joinpath("docs", "make.jl"))' diff --git a/README.md b/README.md index b434cc6d..4107c7de 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ saver = train.Saver() # Run training run(sess, global_variables_initializer()) checkpoint_path = mktempdir() -info("Checkpoint files saved in $checkpoint_path") +@info("Checkpoint files saved in $checkpoint_path") for epoch in 1:100 cur_loss, _ = run(sess, [Loss, minimize_op], Dict(X=>x, Y_obs=>y)) println(@sprintf("Current loss is %.2f.", cur_loss)) diff --git a/REQUIRE b/REQUIRE index ba2e01e1..770f54ed 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,12 +1,16 @@ -julia 0.6 -ProtoBuf 0.3.0 +julia 0.7 +ProtoBuf 0.6.1 PyCall 1.7.1 -TakingBroadcastSeriously 0.1.1 Conda 0.6.0 +Distributions 0.10.2 StatsFuns 0.3.0 +SpecialFunctions v0.7.0 JLD2 0.0.6 FileIO 0.1.2 Juno 0.2.3 Compat 0.18 MacroTools 0.3.6 AutoHashEquals 0.1.0 +MNIST 0.0.2 +Nullables 0.0.7 +SpecialFunctions 0.7.0 diff --git a/deps/.gitignore b/deps/.gitignore new file mode 100644 index 00000000..751b1d07 --- /dev/null +++ b/deps/.gitignore @@ -0,0 +1 @@ +build.log diff --git a/deps/build.jl b/deps/build.jl index 0ce0604f..8e1c133f 100644 --- a/deps/build.jl +++ b/deps/build.jl @@ -9,7 +9,7 @@ const cur_py_version = "1.8.0" # Error message for Windows ############################ -if is_windows() +if Sys.iswindows() error("TensorFlow.jl does not support Windows. Please see https://github.com/malmaud/TensorFlow.jl/issues/204") end @@ -19,15 +19,15 @@ end use_gpu = "TF_USE_GPU" ∈ keys(ENV) && ENV["TF_USE_GPU"] == "1" -if is_apple() && use_gpu - warn("No support for TF_USE_GPU on OS X - to enable the GPU, build TensorFlow from source. Falling back to CPU") +if Sys.isapple() && use_gpu + @warn("No support for TF_USE_GPU on OS X - to enable the GPU, build TensorFlow from source. Falling back to CPU") use_gpu=false end if use_gpu - info("Building TensorFlow.jl for use on the GPU") + @info("Building TensorFlow.jl for use on the GPU") else - info("Building TensorFlow.jl for CPU use only. To enable the GPU, set the TF_USE_GPU environment variable to 1 and rebuild TensorFlow.jl") + @info("Building TensorFlow.jl for CPU use only. To enable the GPU, set the TF_USE_GPU environment variable to 1 and rebuild TensorFlow.jl") end @@ -79,24 +79,24 @@ function download_and_unpack(url) run(`tar -xzf $tensorflow_zip_path -C downloads`) end -@static if is_apple() +@static if Sys.isapple() if use_gpu url = "https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-gpu-darwin-x86_64-$cur_version.tar.gz" else url = "https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-cpu-darwin-x86_64-$cur_version.tar.gz" end download_and_unpack(url) - mv("$lib_dir/libtensorflow.so", "usr/bin/libtensorflow.dylib", remove_destination=true) - mv("$lib_dir/libtensorflow_framework.so", "usr/bin/libtensorflow_framework.so", remove_destination=true) + mv("$lib_dir/libtensorflow.so", "usr/bin/libtensorflow.dylib", force=true) + mv("$lib_dir/libtensorflow_framework.so", "usr/bin/libtensorflow_framework.so", force=true) end -@static if is_linux() +@static if Sys.islinux() if use_gpu url = "https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-gpu-linux-x86_64-$cur_version.tar.gz" else url = "https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-cpu-linux-x86_64-$cur_version.tar.gz" end download_and_unpack(url) - mv("$lib_dir/libtensorflow.so", "usr/bin/libtensorflow.so", remove_destination=true) - mv("$lib_dir/libtensorflow_framework.so", "usr/bin/libtensorflow_framework.so", remove_destination=true) + mv("$lib_dir/libtensorflow.so", "usr/bin/libtensorflow.so", force=true) + mv("$lib_dir/libtensorflow_framework.so", "usr/bin/libtensorflow_framework.so", force=true) end diff --git a/docs/src/logistic.md b/docs/src/logistic.md index bf5b1a54..fc4d009d 100644 --- a/docs/src/logistic.md +++ b/docs/src/logistic.md @@ -39,7 +39,7 @@ saver = train.Saver() # Run training run(sess, global_variables_initializer()) checkpoint_path = mktempdir() -info("Checkpoint files saved in $checkpoint_path") +@info("Checkpoint files saved in $checkpoint_path") for epoch in 1:100 cur_loss, _ = run(sess, (Loss, minimize_op), Dict(X=>x, Y_obs=>y)) println(@sprintf("Current loss is %.2f.", cur_loss)) diff --git a/docs/src/tutorial.md b/docs/src/tutorial.md index 907c35b6..3e44975f 100644 --- a/docs/src/tutorial.md +++ b/docs/src/tutorial.md @@ -64,7 +64,7 @@ end ### Evaluate the model ```julia -correct_prediction = indmax(y, 2) .== indmax(y_, 2) +correct_prediction = argmax(y, 2) .== argmax(y_, 2) accuracy=reduce_mean(cast(correct_prediction, Float32)) testx, testy = load_test_set() @@ -137,7 +137,7 @@ cross_entropy = reduce_mean(-reduce_sum(y_.*log(y_conv), axis=[2])) train_step = train.minimize(train.AdamOptimizer(1e-4), cross_entropy) -correct_prediction = indmax(y_conv, 2) .== indmax(y_, 2) +correct_prediction = argmax(y_conv, 2) .== argmax(y_, 2) accuracy = reduce_mean(cast(correct_prediction, Float32)) @@ -147,12 +147,12 @@ for i in 1:1000 batch = next_batch(loader, 50) if i%100 == 1 train_accuracy = run(session, accuracy, Dict(x=>batch[1], y_=>batch[2], keep_prob=>1.0)) - info("step $i, training accuracy $train_accuracy") + @info("step $i, training accuracy $train_accuracy") end run(session, train_step, Dict(x=>batch[1], y_=>batch[2], keep_prob=>.5)) end testx, testy = load_test_set() test_accuracy = run(session, accuracy, Dict(x=>testx, y_=>testy, keep_prob=>1.0)) -info("test accuracy $test_accuracy") +@info("test accuracy $test_accuracy") ``` diff --git a/examples/ae.jl b/examples/ae.jl index e9bc8feb..e4e6eda5 100644 --- a/examples/ae.jl +++ b/examples/ae.jl @@ -23,8 +23,8 @@ end num_features = 28^2 -include(Pkg.dir("TensorFlow","examples","mnist_loader.jl")) -include(Pkg.dir("TensorFlow","src","layers","fully_connected.jl")) +include(joinpath(dirname(pathof(TensorFlow)), "..", "examples","mnist_loader.jl")) +include(joinpath(dirname(pathof(TensorFlow)), "..", "src","layers","fully_connected.jl")) loader = DataLoader() session = Session(Graph()) @@ -77,7 +77,7 @@ history = MVHistory() push!(history,:loss_val, epoch, val_loss) plot(history, reuse=true) scatter3d(center[:,1],center[:,2],center[:,3], zcolor = testy, legend=false, title="Latent space", reuse=true) - info("step $epoch, training loss $train_loss, time taken: $(printtime(t0))") + @info("step $epoch, training loss $train_loss, time taken: $(printtime(t0))") train.save(saver, session, joinpath(checkpoint_path, "ae_mnist"), global_step=epoch) end run(session, train_step, Dict(x=>batch)) @@ -85,7 +85,7 @@ history = MVHistory() end test_loss, center, reconstruction = run(session, [loss_MSE, l_z, l_out], Dict(x=>testx)) -info("test accuracy $test_loss") +@info("test accuracy $test_loss") # Plot som example reconstructions offset = 0 diff --git a/examples/logistic.jl b/examples/logistic.jl index 740dae02..d1f85ff9 100644 --- a/examples/logistic.jl +++ b/examples/logistic.jl @@ -1,11 +1,12 @@ using TensorFlow using Distributions +using Printf # Generate some synthetic data x = randn(100, 50) w = randn(50, 10) y_prob = exp.(x*w) -y_prob ./= sum(y_prob,2) +y_prob ./= sum(y_prob,dims=2) function draw(probs) y = zeros(size(probs)) @@ -39,7 +40,7 @@ saver = train.Saver() # Run training run(sess, global_variables_initializer()) checkpoint_path = mktempdir() -info("Checkpoint files saved in $checkpoint_path") +@info("Checkpoint files saved in $checkpoint_path") for epoch in 1:100 cur_loss, _ = run(sess, [Loss, minimize_op], Dict(X=>x, Y_obs=>y)) println(@sprintf("Current loss is %.2f.", cur_loss)) diff --git a/examples/mnist_full.jl b/examples/mnist_full.jl index ad37c951..4ce1f1f2 100644 --- a/examples/mnist_full.jl +++ b/examples/mnist_full.jl @@ -62,7 +62,7 @@ end train_step = train.minimize(train.AdamOptimizer(1e-4), cross_entropy) -correct_prediction = indmax(y_conv, 2) .== indmax(y_, 2) +correct_prediction = argmax(y_conv, 2) .== argmax(y_, 2) accuracy = reduce_mean(cast(correct_prediction, Float32)) @@ -72,13 +72,13 @@ for i in 1:200 batch = next_batch(loader, 50) if i%100 == 1 train_accuracy = run(session, accuracy, Dict(x=>batch[1], y_=>batch[2], keep_prob=>1.0)) - info("step $i, training accuracy $train_accuracy") + @info("step $i, training accuracy $train_accuracy") end run(session, train_step, Dict(x=>batch[1], y_=>batch[2], keep_prob=>.5)) end testx, testy = load_test_set() test_accuracy = run(session, accuracy, Dict(x=>testx, y_=>testy, keep_prob=>1.0)) -info("test accuracy $test_accuracy") +@info("test accuracy $test_accuracy") visualize() diff --git a/examples/mnist_loader.jl b/examples/mnist_loader.jl index 64006262..2a5d84b9 100644 --- a/examples/mnist_loader.jl +++ b/examples/mnist_loader.jl @@ -1,11 +1,12 @@ using MNIST +import Random mutable struct DataLoader cur_id::Int order::Vector{Int} end -DataLoader() = DataLoader(1, shuffle(1:60000)) +DataLoader() = DataLoader(1, Random.shuffle(1:60000)) function next_batch(loader::DataLoader, batch_size) x = zeros(Float32, batch_size, 784) diff --git a/examples/mnist_simple.jl b/examples/mnist_simple.jl index 47b99c4e..c2da33bc 100644 --- a/examples/mnist_simple.jl +++ b/examples/mnist_simple.jl @@ -18,7 +18,7 @@ y = nn.softmax(x*W + b) cross_entropy = reduce_mean(-reduce_sum(y_ .* log(y), axis=[2])) train_step = train.minimize(train.GradientDescentOptimizer(.00001), cross_entropy) -correct_prediction = indmax(y, 2) .== indmax(y_, 2) +correct_prediction = argmax(y, 2) .== argmax(y_, 2) accuracy=reduce_mean(cast(correct_prediction, Float32)) for i in 1:1000 diff --git a/src/TensorFlow.jl b/src/TensorFlow.jl index bf9cabd8..e7f5e0ef 100644 --- a/src/TensorFlow.jl +++ b/src/TensorFlow.jl @@ -1,7 +1,6 @@ -__precompile__(true) module TensorFlow -warn("Loading a new version of TensorFlow.jl for the first time. This initial load can take around 5 minutes as code is precompiled; subsequent usage will only take a few seconds.") +@warn("Loading a new version of TensorFlow.jl for the first time. This initial load can take around 5 minutes as code is precompiled; subsequent usage will only take a few seconds.") export Graph, @@ -127,10 +126,17 @@ import_op, @tfimport, tf_versioninfo + +using Distributed + const pyproc = Ref(0) +function deallocator(data, len, arg) + +end + function __init__() - c_deallocator[] = cfunction(deallocator, Void, (Ptr{Void}, Csize_t, Ptr{Void})) + c_deallocator[] = @cfunction(deallocator, Cvoid, (Ptr{Cvoid}, Csize_t, Ptr{Cvoid})) end function load_python_process(;force_reload=false) @@ -139,8 +145,9 @@ function load_python_process(;force_reload=false) addprocs(1) pyproc[] = nprocs() py_file = joinpath(dirname(@__FILE__), "py.jl") - eval(Main, quote + Base.eval(Main, quote # These have to be split for unclear reasons on .6 + using Distributed remotecall_wait($(pyproc[]), $py_file) do py_file include(py_file) end @@ -151,7 +158,7 @@ function load_python_process(;force_reload=false) py_version_check() return pyproc[] else - remotecall_fetch(1) do + Distributed.remotecall_fetch(1) do load_python_process() end end @@ -169,7 +176,7 @@ Returns a future to the result. """ macro py_proc(expr) quote - eval(Main, quote + Base.eval(Main, quote remotecall_wait($(TensorFlow.load_python_process())) do $($(Expr(:quote, expr))) end diff --git a/src/core.jl b/src/core.jl index 46c5217d..799f588c 100644 --- a/src/core.jl +++ b/src/core.jl @@ -4,6 +4,8 @@ using Compat using Compat.Iterators using MacroTools using AutoHashEquals +using Nullables +using Libdl import Base: setindex!, getindex, run, == @@ -46,19 +48,19 @@ end mutable struct Status - ptr::Ptr{Void} + ptr::Ptr{Cvoid} function Status() - ptr = @tfcall(:TF_NewStatus, Ptr{Void}, ()) + ptr = @tfcall(:TF_NewStatus, Ptr{Cvoid}, ()) this = new(ptr) - finalizer(this, status->begin - @tfcall(:TF_DeleteStatus, Void, (Ptr{Void},), status.ptr) - end) + finalizer(this) do status + @tfcall(:TF_DeleteStatus, Cvoid, (Ptr{Cvoid},), status.ptr) + end this end end function get_code(s::Status) - code = @tfcall(:TF_GetCode, Cint, (Ptr{Void},), s.ptr) + code = @tfcall(:TF_GetCode, Cint, (Ptr{Cvoid},), s.ptr) return TF_Code(code) end @@ -82,11 +84,7 @@ function DevicePart(s::AbstractString) name = String(parts[1]) index_part = String(parts[2]) maybe_index = tryparse(Int, index_part) - if isnull(maybe_index) - index = index_part - else - index = get(maybe_index) - end + index=something(maybe_index, index_part) DevicePart(name, index) end @@ -163,14 +161,14 @@ end function TensorShape(dims::AbstractVector{<:Integer}) - TensorShape([x<0 ? Nullable{Int}() : Nullable{Int}(x) for x in dims]) + TensorShape([x<0 ? Nullable{Int64}() : Nullable{Int64}(Int64(x)) for x in dims]) end function TensorShape(dims) TensorShape(dims, false) end -function TensorShape(::Void) +function TensorShape(::Cvoid) TensorShape(Nullable{Int}[], true) end @@ -186,14 +184,14 @@ function get_shape end A TensorFlow computation graph """ mutable struct Graph - ptr::Ptr{Void} + ptr::Ptr{Cvoid} collections::Dict{Symbol, Any} shapes::Dict{String, TensorShape} name_idx::Dict{String, Int} op_context::OperationContext function Graph() - ptr = @tfcall(:TF_NewGraph, Ptr{Void}, ()) + ptr = @tfcall(:TF_NewGraph, Ptr{Cvoid}, ()) collections = Dict{Symbol, Any}() collections[:Variables] = [] collections[:TrainableVariables] = [] @@ -201,9 +199,9 @@ mutable struct Graph collections[:QueueRunners] = [] collections[:while_context] = [] self = new(ptr, collections, Dict{String, TensorShape}(), Dict{String, Int}(), OperationContext(Vector{Operation}[], String[], tensorflow.WhileContextDef[], Device[], Ref(false))) - finalizer(self, self->begin - @tfcall(:TF_DeleteGraph, Void, (Ptr{Void},), self.ptr) - end) + finalizer(self) do self + @tfcall(:TF_DeleteGraph, Cvoid, (Ptr{Cvoid},), self.ptr) + end self end end @@ -271,7 +269,7 @@ end const DEBUG_EXTEND_GRAPH = false -function Base.convert(::Type{tensorflow.NodeDef}, proto::Vector{UInt8}) +function Base.convert(::Type{tensorflow.NodeDef}, proto::DenseVector{UInt8}) b = IOBuffer() write(b, proto) seekstart(b) @@ -363,10 +361,10 @@ end mutable struct SessionOptions - ptr::Ptr{Void} + ptr::Ptr{Cvoid} function SessionOptions() - ptr = @tfcall(:TF_NewSessionOptions, Ptr{Void}, ()) + ptr = @tfcall(:TF_NewSessionOptions, Ptr{Cvoid}, ()) self = new(ptr) set_tf_finalizer(self) self @@ -374,9 +372,9 @@ mutable struct SessionOptions end function set_tf_finalizer(options::SessionOptions) - finalizer(options, options->begin - @tfcall(:TF_DeleteSessionOptions, Void, (Ptr{Void},), options.ptr) - end) + finalizer(options) do options + @tfcall(:TF_DeleteSessionOptions, Cvoid, (Ptr{Cvoid},), options.ptr) + end options end @@ -398,7 +396,7 @@ const upgrade_check_needed = Ref(true) function upgrade_check(v) if upgrade_check_needed[] if tf_version() < v - warn("You are using an old version version of the TensorFlow binary library. It is recommened that you upgrade with Pkg.build(\"TensorFlow\") or various + @warn("You are using an old version version of the TensorFlow binary library. It is recommened that you upgrade with Pkg.build(\"TensorFlow\") or various errors may be encountered.\n You have $(tf_version()) and the new version is $v.") end upgrade_check_needed[] = false @@ -464,7 +462,7 @@ end A TensorFlow session. """ mutable struct Session - ptr::Ptr{Void} + ptr::Ptr{Cvoid} graph::Graph function Session(graph, config=nothing) @@ -476,16 +474,16 @@ mutable struct Session seekstart(b) proto = read(b) config_status = Status() - @tfcall(:TF_SetConfig, Void, (Ptr{Void}, Ptr{Void}, Csize_t, Ptr{Void}), options.ptr, proto, sizeof(proto), config_status.ptr) + @tfcall(:TF_SetConfig, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t, Ptr{Cvoid}), options.ptr, proto, sizeof(proto), config_status.ptr) check_status(config_status) end status = Status() - ptr = @tfcall(:TF_NewSession, Ptr{Void}, (Ptr{Void}, Ptr{Void}, Ptr{Void}), graph.ptr, options.ptr, status.ptr) + ptr = @tfcall(:TF_NewSession, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}), graph.ptr, options.ptr, status.ptr) this = new(ptr, graph) check_status(status) - finalizer(this, self->begin + finalizer(this) do self close(self) - end) + end return this end @@ -508,7 +506,7 @@ Closes the TensorFlow session, freeing the associated computational resources. function Base.close(sess::Session) if sess.ptr != C_NULL status = Status() - @tfcall(:TF_DeleteSession, Void, (Ptr{Void}, Ptr{Void}), sess.ptr, status.ptr) + @tfcall(:TF_DeleteSession, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), sess.ptr, status.ptr) check_status(status) sess.ptr = C_NULL end @@ -517,18 +515,18 @@ end mutable struct Buffer - ptr::Ptr{Void} + ptr::Ptr{Cvoid} function Buffer(s::Vector{UInt8}) self = new() - self.ptr = @tfcall(:TF_NewBufferFromString, Ptr{Void}, (Ptr{Void}, Csize_t), pointer(s), sizeof(s)) + self.ptr = @tfcall(:TF_NewBufferFromString, Ptr{Cvoid}, (Ptr{Cvoid}, Csize_t), pointer(s), sizeof(s)) set_tf_finalizer(self) return self end function Buffer() self = new() - self.ptr = @tfcall(:TF_NewBuffer, Ptr{Void}, ()) + self.ptr = @tfcall(:TF_NewBuffer, Ptr{Cvoid}, ()) set_tf_finalizer(self) return self end @@ -537,19 +535,19 @@ mutable struct Buffer end function set_tf_finalizer(buffer::Buffer) - finalizer(buffer, buffer->begin - @tfcall(:TF_DeleteBuffer, Void, (Ptr{Void},), buffer.ptr) - end) + finalizer(buffer) do buffer + @tfcall(:TF_DeleteBuffer, Cvoid, (Ptr{Cvoid},), buffer.ptr) + end end struct BufferStruct data::Ptr{UInt8} len::Csize_t - deallocator::Ptr{Void} + deallocator::Ptr{Cvoid} end function getindex(b::Buffer) - @tfcall(:TF_GetBuffer, BufferStruct, (Ptr{Void},), b.ptr) + @tfcall(:TF_GetBuffer, BufferStruct, (Ptr{Cvoid},), b.ptr) end function Base.convert(::Type{Array}, buf::Buffer) @@ -558,10 +556,6 @@ function Base.convert(::Type{Array}, buf::Buffer) copy(array) end -function deallocator(data, len, arg) - -end - const c_deallocator = Ref{Ptr}() """ @@ -581,7 +575,7 @@ function Base.show(io::IO, err::EmptyTensorError) end mutable struct RawTensor - ptr::Ptr{Void} + ptr::Ptr{Cvoid} data::Array # To avoid underlying data being GCed RawTensor() = new() @@ -591,7 +585,7 @@ mutable struct RawTensor dims = [size(data)...] dt = jl_to_df_type(eltype(data)) data = convert_major_order(data) - ptr = @tfcall(:TF_NewTensor, Ptr{Void}, (Cint, Ptr{Cint}, Cint, Ptr{Void}, Csize_t, Ptr{Void}, Ptr{Void}), + ptr = @tfcall(:TF_NewTensor, Ptr{Cvoid}, (Cint, Ptr{Cint}, Cint, Ptr{Cvoid}, Csize_t, Ptr{Cvoid}, Ptr{Cvoid}), Int(dt), pointer(dims), length(dims), @@ -608,7 +602,7 @@ mutable struct RawTensor dims = Cint[] dt = jl_to_df_type(eltype(data)) data_boxed = [data] - ptr = @tfcall(:TF_NewTensor, Ptr{Void}, (Cint, Ptr{Void}, Cint, Ptr{Void}, Csize_t, Ptr{Void}, Ptr{Void}), + ptr = @tfcall(:TF_NewTensor, Ptr{Cvoid}, (Cint, Ptr{Cvoid}, Cint, Ptr{Cvoid}, Csize_t, Ptr{Cvoid}, Ptr{Cvoid}), Int(dt), pointer(dims), length(dims), @@ -629,9 +623,9 @@ mutable struct RawTensor end function set_tf_finalizer(tensor::RawTensor) - finalizer(tensor, tensor->begin - @tfcall(:TF_DeleteTensor, Void, (Ptr{Void},), tensor.ptr) - end) + finalizer(tensor) do tensor + @tfcall(:TF_DeleteTensor, Cvoid, (Ptr{Cvoid},), tensor.ptr) + end end RawTensor(data::AbstractArray) = RawTensor(collect(data)) @@ -663,31 +657,31 @@ function varint_decode(b::IO) return n end -function tf_string_encode(src::Vector{UInt8}) +function tf_string_encode(src::DenseVector{UInt8}) dest_length = @tfcall(:TF_StringEncodedSize, Csize_t, (Csize_t,), length(src)) |> Int - dest = Vector{UInt8}(dest_length) + dest = Vector{UInt8}(undef, dest_length) status = Status() @tfcall(:TF_StringEncode, Csize_t, - (Ptr{Void}, Csize_t, Ptr{Void}, Csize_t, Ptr{Void}), + (Ptr{Cvoid}, Csize_t, Ptr{Cvoid}, Csize_t, Ptr{Cvoid}), src, length(src), dest, length(dest), status.ptr) check_status(status) dest end -tf_string_encode(src) = tf_string_encode(Vector{UInt8}(src)) +tf_string_encode(src) = tf_string_encode(codeunits(src)) -function tf_string_decode(src::Vector{UInt8}) +function tf_string_decode(src::DenseVector{UInt8}) status = Status() dst = Ref{Ptr{UInt8}}() dst_len = Ref{Csize_t}() @tfcall(:TF_StringDecode, Csize_t, - (Ptr{Void}, Csize_t, Ref{Ptr{UInt8}}, Ref{Csize_t}, Ptr{Void}), + (Ptr{Cvoid}, Csize_t, Ref{Ptr{UInt8}}, Ref{Csize_t}, Ptr{Cvoid}), src, length(src), dst, dst_len, status.ptr) check_status(status) copy(unsafe_wrap(Array, dst[], Int(dst_len[]))) end -tf_string_decode(src) = tf_string_decode(Vector{UInt8}(src)) +tf_string_decode(src) = tf_string_decode(codeunits(src)) tf_string_decode(T, src) = T(tf_string_decode(src)) # cf this section of c_api.h in upstream tensorflow/c_api.h @@ -733,7 +727,7 @@ function RawTensor(data::Array{String}, is_scalar=false) end data_encoded = take!(encoded_buf) dt = jl_to_df_type(String) - ptr = @tfcall(:TF_NewTensor, Ptr{Void}, (Cint, Ptr{Int64}, Cint, Ptr{Void}, Csize_t, Ptr{Void}, Ptr{Void}), + ptr = @tfcall(:TF_NewTensor, Ptr{Cvoid}, (Cint, Ptr{Int64}, Cint, Ptr{Cvoid}, Csize_t, Ptr{Cvoid}, Ptr{Cvoid}), Int(dt), dims, length(dims), @@ -759,39 +753,39 @@ end function Base.ndims(t::RawTensor) - @tfcall(:TF_NumDims, Cint, (Ptr{Void},), t.ptr) |> Int + @tfcall(:TF_NumDims, Cint, (Ptr{Cvoid},), t.ptr) |> Int end function Base.size(t::RawTensor, dim::Integer) n = ndims(t) dim -= 1 @assert dim < n - @tfcall(:TF_Dim, Clonglong, (Ptr{Void}, Cint), t.ptr, dim) + @tfcall(:TF_Dim, Clonglong, (Ptr{Cvoid}, Cint), t.ptr, dim) end function Base.size(t::RawTensor) d = (size(t, x) for x in 1:ndims(t)) - (d...) + (d...,) end function Base.sizeof(t::RawTensor) - @tfcall(:TF_TensorByteSize, Csize_t, (Ptr{Void},), t.ptr) |> Int + @tfcall(:TF_TensorByteSize, Csize_t, (Ptr{Cvoid},), t.ptr) |> Int end function set_device(node_desc, device::String) - @tfcall(:TF_SetDevice, Void, - (Ptr{Void}, Cstring), + @tfcall(:TF_SetDevice, Cvoid, + (Ptr{Cvoid}, Cstring), node_desc.ptr, device) end set_device(node_desc, device::Device) = set_device(node_desc, device_index_from_zero(device)) mutable struct NodeDescription - ptr::Ptr{Void} + ptr::Ptr{Cvoid} graph::Graph function NodeDescription(graph, op_type, full_name) - desc = @tfcall(:TF_NewOperation, Ptr{Void}, (Ptr{Void}, Cstring, Cstring), graph.ptr, op_type, full_name) + desc = @tfcall(:TF_NewOperation, Ptr{Cvoid}, (Ptr{Cvoid}, Cstring, Cstring), graph.ptr, op_type, full_name) self = new(desc, graph) for control_op_set in graph.op_context.control_ops for control_op in control_op_set @@ -823,7 +817,7 @@ abstract type AbstractOperation end An operation in the computation graph. """ mutable struct Operation <: AbstractOperation - ptr::Ptr{Void} + ptr::Ptr{Cvoid} graph::Nullable{Graph} op_name::String name::String @@ -834,12 +828,12 @@ end Base.hash(op::Operation, h::UInt) = hash(Operation, hash(op.ptr, h)) struct Port - node_ptr::Ptr{Void} + node_ptr::Ptr{Cvoid} index::Int end function get_num_inputs(op::Operation) - @tfcall(:TF_OperationNumInputs, Cint, (Ptr{Void},), op.ptr) |> Int + @tfcall(:TF_OperationNumInputs, Cint, (Ptr{Cvoid},), op.ptr) |> Int end struct InputOutOfRangeError <: Exception @@ -868,13 +862,13 @@ function get_input(op::Operation, idx) end function get_num_control_inputs(op::Operation) - @tfcall(:TF_OperationNumControlInputs, Cint, (Ptr{Void},), op.ptr) |> Int + @tfcall(:TF_OperationNumControlInputs, Cint, (Ptr{Cvoid},), op.ptr) |> Int end function get_control_inputs(op::Operation) N = get_num_control_inputs(op) - ptrs = Vector{Ptr{Void}}(N) - N_out = @tfcall(:TF_OperationGetControlInputs, Cint, (Ptr{Void}, Ptr{Ptr{Void}}, Cint), + ptrs = Vector{Ptr{Cvoid}}(undef, N) + N_out = @tfcall(:TF_OperationGetControlInputs, Cint, (Ptr{Cvoid}, Ptr{Ptr{Cvoid}}, Cint), op.ptr, ptrs, N) out = Vector{Operation}() for n in 1:N_out @@ -888,7 +882,7 @@ end function get_input_list_length(op::Operation, arg_name) status = Status() - out = @tfcall(:TF_OperationInputListLength, Cint, (Ptr{Void}, Cstring, Ptr{Void}), op.ptr, arg_name, status.ptr) + out = @tfcall(:TF_OperationInputListLength, Cint, (Ptr{Cvoid}, Cstring, Ptr{Cvoid}), op.ptr, arg_name, status.ptr) check_status(status) Int(out) end @@ -902,7 +896,7 @@ end function get_attr_metadata(op::Operation, attr) status = Status() - out = @tfcall(:TF_OperationGetAttrMetadata, AttrMetadata, (Ptr{Void}, Cstring, Ptr{Void}), op.ptr, attr, status.ptr) + out = @tfcall(:TF_OperationGetAttrMetadata, AttrMetadata, (Ptr{Cvoid}, Cstring, Ptr{Cvoid}), op.ptr, attr, status.ptr) check_status(status) out end @@ -910,48 +904,48 @@ end function get_attr(op::Operation, attr, ::Type{Int}) out = Ref{Int}() status = Status() - @tfcall(:TF_OperationGetAttrInt, Void, (Ptr{Void}, Cstring, Ref{Int}, Ptr{Void}), op.ptr, attr, out, status.ptr) + @tfcall(:TF_OperationGetAttrInt, Cvoid, (Ptr{Cvoid}, Cstring, Ref{Int}, Ptr{Cvoid}), op.ptr, attr, out, status.ptr) check_status(status) out[] end function get_attr(op::Operation, attr, ::Type{Array}) - out = Ref{Ptr{Void}}() + out = Ref{Ptr{Cvoid}}() status = Status() - @tfcall(:TF_OperationGetAttrTensor, Void, (Ptr{Void}, Cstring, Ptr{Ptr{Void}}, Ptr{Void}), op.ptr, attr, out, status.ptr) + @tfcall(:TF_OperationGetAttrTensor, Cvoid, (Ptr{Cvoid}, Cstring, Ptr{Ptr{Cvoid}}, Ptr{Cvoid}), op.ptr, attr, out, status.ptr) check_status(status) - Array(RawTensor(out[])) + convert(Array, RawTensor(out[])) end function get_attr(op::Operation, attr, ::Type{Bool}) out = Ref{Bool}() status = Status() - @tfcall(:TF_OperationGetAttrBool, Void, (Ptr{Void}, Cstring, Ref{Bool}, Ptr{Void}), op.ptr, attr, out, status.ptr) + @tfcall(:TF_OperationGetAttrBool, Cvoid, (Ptr{Cvoid}, Cstring, Ref{Bool}, Ptr{Cvoid}), op.ptr, attr, out, status.ptr) check_status(status) out[] end function get_attr(op::Operation, attr, ::Type{Vector{Int}}) meta = get_attr_metadata(op, attr) - out = Vector{Int}(meta.list_size) + out = Vector{Int}(undef, meta.list_size) status = Status() - @tfcall(:TF_OperationGetAttrIntList, Void, (Ptr{Void}, Cstring, Ptr{Int}, Cint, Ptr{Void}), op.ptr, attr, out, length(out), status.ptr) + @tfcall(:TF_OperationGetAttrIntList, Cvoid, (Ptr{Cvoid}, Cstring, Ptr{Int}, Cint, Ptr{Cvoid}), op.ptr, attr, out, length(out), status.ptr) check_status(status) out end function get_attr(op::Operation, attr, ::Type{String}) meta = get_attr_metadata(op, attr) - out = Vector{UInt8}(meta.total_size) + out = Vector{UInt8}(undef, meta.total_size) status = Status() - @tfcall(:TF_OperationGetAttrString, Void, (Ptr{Void}, Cstring, Ptr{UInt8}, Cint, Ptr{Void}), op.ptr, attr, out, length(out), status.ptr) + @tfcall(:TF_OperationGetAttrString, Cvoid, (Ptr{Cvoid}, Cstring, Ptr{UInt8}, Cint, Ptr{Cvoid}), op.ptr, attr, out, length(out), status.ptr) check_status(status) String(out) end function fillin(op::Operation) - op.name = @tfcall(:TF_OperationName, Cstring, (Ptr{Void},), op.ptr) |> unsafe_string - op.op_name = @tfcall(:TF_OperationOpType, Cstring, (Ptr{Void},), op.ptr) |> unsafe_string + op.name = @tfcall(:TF_OperationName, Cstring, (Ptr{Cvoid},), op.ptr) |> unsafe_string + op.op_name = @tfcall(:TF_OperationOpType, Cstring, (Ptr{Cvoid},), op.ptr) |> unsafe_string end function with_op_name(f, name, def_name="Node") @@ -1014,7 +1008,7 @@ end function Operation(desc::NodeDescription) self = Operation() status = Status() - ptr = @tfcall(:TF_FinishOperation, Ptr{Void}, (Ptr{Void}, Ptr{Void}), desc.ptr, status.ptr) + ptr = @tfcall(:TF_FinishOperation, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}), desc.ptr, status.ptr) check_status(status) self.ptr = ptr graph = desc.graph @@ -1048,7 +1042,7 @@ get_graph(n::AbstractOperation) = Operation(n).graph function load_proto(tensor::tensorflow.TensorProto) dtype = tensor.dtype - dim = (Int[x.size for x in tensor.tensor_shape.dim]...) + dim = (Int[x.size for x in tensor.tensor_shape.dim]...,) if dtype == tensorflow._DataType.DT_FLOAT val = tensor.float_val elseif dtype == tensorflow._DataType.DT_INT32 @@ -1148,19 +1142,19 @@ function load_proto(value::tensorflow.AttrValue) end """ - node_name(node::AbstractOperation) + node_name(node::Operation) Returns the name of a node in the computation graph. """ -node_name(node::AbstractOperation) = @tfcall(:TF_OperationName, Cstring, (Ptr{Void},), Operation(node).ptr) |> unsafe_string +node_name(node::Operation) = @tfcall(:TF_OperationName, Cstring, (Ptr{Cvoid},), node.ptr) |> unsafe_string function get_attr_value_proto(node::Operation, attr_name) buf = Buffer() status = Status() - @tfcall(:TF_OperationGetAttrValueProto, Void, (Ptr{Void}, Cstring, Ptr{Void}, Ptr{Void}), node.ptr, attr_name, buf.ptr, status.ptr) + @tfcall(:TF_OperationGetAttrValueProto, Cvoid, (Ptr{Cvoid}, Cstring, Ptr{Cvoid}, Ptr{Cvoid}), node.ptr, attr_name, buf.ptr, status.ptr) check_status(status) - proto = Array(buf) + proto = convert(Array, buf) b = IOBuffer() write(b, proto) seekstart(b) @@ -1180,8 +1174,8 @@ const proto_type_map = Dict( dt.DT_STRING=>String, dt.DT_BOOL=>Bool, dt.DT_UINT8=>UInt8, - dt.DT_COMPLEX64=>Complex64, - dt.DT_COMPLEX128=>Complex128) + dt.DT_COMPLEX64=>ComplexF32, + dt.DT_COMPLEX128=>ComplexF64) abstract type AbstractTensor{T} end @@ -1202,18 +1196,18 @@ end Tensor(op::Operation) = Tensor(op, 1) +Tensor(value) = convert(Tensor, value) +Tensor{T}(value) where {T} = convert(Tensor{T}, value) + Base.convert(::Type{Tensor{T}}, value::AbstractTensor) where {T} = convert(Tensor{T}, convert(Tensor, value)) Base.convert(::Type{Tensor}, value) = constant(value) Base.convert(::Type{Tensor}, value::Tensor) = value Base.convert(::Type{Tensor{T}}, value::Tensor{R}) where {T, R} = cast(value, T) Base.convert(::Type{Tensor{T}}, value::Tensor{T}) where {T} = value -Base.convert(::Type{Tensor{Any}}, value::Tensor{R}) where {R} = convert(Tensor, value) -Base.convert(::Type{Tensor{Any}}, value::Tensor{Any}) = value +Base.convert(::Type{Tensor{Any}}, value::Tensor{R}) where {R} = value -function Base.convert(::Type{Tensor{T}}, value) where T - convert(Tensor{T}, constant(value)) -end +Base.convert(::Type{Tensor{T}}, value) where {T} = convert(Tensor{T}, constant(value)) function operation_output_type(port::Port) @tfcall(:TF_OperationOutputType, TF_DataType, (Port,), port) @@ -1225,10 +1219,12 @@ function get_output_type(t::AbstractTensor) local dtype try dtype = get_op(t)["T"]._type - catch + catch err1 + err1 isa TFException || rethrow(err1) try dtype = get_op(t)["dtype"]._type - catch + catch err2 + err2 isa TFException || rethrow(err2) return Any end end @@ -1238,7 +1234,7 @@ function get_output_type(t::AbstractTensor) end end -Base.eltype(::Type{AbstractTensor{T}}) where {T} = T +Base.eltype(::Type{<:AbstractTensor{T}}) where {T} = T Port(t::Tensor) = Port(t.op.ptr, t.value_index-1) Port(op::Operation) = Port(Tensor(op)) @@ -1247,39 +1243,39 @@ Port(port::Port) = port Tensor(p::Port) = Tensor(Operation(p.node_ptr), p.index+1) function add_input(desc::NodeDescription, input::Union{Tensor, Operation}) - @tfcall(:TF_AddInput, Void, (Ptr{Void}, Port), desc.ptr, Port(input)) + @tfcall(:TF_AddInput, Cvoid, (Ptr{Cvoid}, Port), desc.ptr, Port(input)) end function add_input(desc::NodeDescription, inputs::Vector) inputs = map(Port, inputs) - @tfcall(:TF_AddInputList, Void, (Ptr{Void}, Ptr{Void}, Cint), desc.ptr, inputs, length(inputs)) + @tfcall(:TF_AddInputList, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}, Cint), desc.ptr, inputs, length(inputs)) end function add_control_input(desc::NodeDescription, op::Operation) - @tfcall(:TF_AddControlInput, Void, (Ptr{Void}, Ptr{Void}), desc.ptr, op.ptr) + @tfcall(:TF_AddControlInput, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), desc.ptr, op.ptr) end add_control_input(desc::NodeDescription, t::Tensor) = add_control_input(desc, t.op) function setindex!(desc::NodeDescription, tensor::RawTensor, attr_name) status = Status() - @tfcall(:TF_SetAttrTensor, Void, (Ptr{Void}, Cstring, Ptr{Void}, Ptr{Void}), desc.ptr, attr_name, tensor.ptr, status.ptr) + @tfcall(:TF_SetAttrTensor, Cvoid, (Ptr{Cvoid}, Cstring, Ptr{Cvoid}, Ptr{Cvoid}), desc.ptr, attr_name, tensor.ptr, status.ptr) check_status(status) end function setindex!(desc::NodeDescription, tensors::Vector{RawTensor}, attr_name) status = Status() - @tfcall(:TF_SetAttrTensorList, Void, (Ptr{Void}, Cstring, Ptr{Ptr{Void}}, Cint, Ptr{Void}), desc.ptr, attr_name, [x.ptr for x in tensors], length(tensors), status.ptr) + @tfcall(:TF_SetAttrTensorList, Cvoid, (Ptr{Cvoid}, Cstring, Ptr{Ptr{Cvoid}}, Cint, Ptr{Cvoid}), desc.ptr, attr_name, [x.ptr for x in tensors], length(tensors), status.ptr) check_status(status) end function setindex!(desc::NodeDescription, dtype::DataType, attr_name) - @tfcall(:TF_SetAttrType, Void, (Ptr{Void}, Cstring, TF_DataType), desc.ptr, attr_name, dtype|>jl_to_df_type) + @tfcall(:TF_SetAttrType, Cvoid, (Ptr{Cvoid}, Cstring, TF_DataType), desc.ptr, attr_name, dtype|>jl_to_df_type) end function setindex!(desc::NodeDescription, value::Integer, attr_name) value = Int64(value) - @tfcall(:TF_SetAttrInt, Void, (Ptr{Void}, Cstring, Int64), desc.ptr, attr_name, value) + @tfcall(:TF_SetAttrInt, Cvoid, (Ptr{Cvoid}, Cstring, Int64), desc.ptr, attr_name, value) end function setindex!(desc::NodeDescription, value::TensorShape, attr_name) @@ -1288,21 +1284,21 @@ function setindex!(desc::NodeDescription, value::TensorShape, attr_name) else dims = Int[(isnull(dim) ? -1 : get(dim)) for dim in value.dims] end - @tfcall(:TF_SetAttrShape, Void, (Ptr{Void}, Cstring, Ptr{Int64}, Cint), desc.ptr, attr_name, dims, length(dims)) + @tfcall(:TF_SetAttrShape, Cvoid, (Ptr{Cvoid}, Cstring, Ptr{Int64}, Cint), desc.ptr, attr_name, dims, length(dims)) end function setindex!(desc::NodeDescription, value::Bool, attr_name) - @tfcall(:TF_SetAttrBool, Void, (Ptr{Void}, Cstring, Cuchar), desc.ptr, attr_name, value) + @tfcall(:TF_SetAttrBool, Cvoid, (Ptr{Cvoid}, Cstring, Cuchar), desc.ptr, attr_name, value) end function setindex!(desc::NodeDescription, value::AbstractFloat, attr_name) value = Float32(value) - @tfcall(:TF_SetAttrFloat, Void, (Ptr{Void}, Cstring, Cfloat), desc.ptr, attr_name, value) + @tfcall(:TF_SetAttrFloat, Cvoid, (Ptr{Cvoid}, Cstring, Cfloat), desc.ptr, attr_name, value) end function setindex!(desc::NodeDescription, value::AbstractString, attr_name) value = String(value) - @tfcall(:TF_SetAttrString, Void, (Ptr{Void}, Cstring, Ptr{Void}, Cint), desc.ptr, attr_name, Vector{UInt8}(value), sizeof(value)) + @tfcall(:TF_SetAttrString, Cvoid, (Ptr{Cvoid}, Cstring, Ptr{Cvoid}, Cint), desc.ptr, attr_name, Vector{UInt8}(value), sizeof(value)) end function setindex!(desc::NodeDescription, value::Vector, attr_name) @@ -1311,17 +1307,17 @@ end function set_attr_list(desc::NodeDescription, attr_name, list::Vector{<:Integer}) list = Int64[Int64(x) for x in list] - @tfcall(:TF_SetAttrIntList, Void, (Ptr{Void}, Cstring, Ptr{Int64}, Cint), desc.ptr, attr_name, list, length(list)) + @tfcall(:TF_SetAttrIntList, Cvoid, (Ptr{Cvoid}, Cstring, Ptr{Int64}, Cint), desc.ptr, attr_name, list, length(list)) end function set_attr_list(desc::NodeDescription, attr_name, list::Vector{<:AbstractFloat}) list = Float32[Float32(x) for x in list] - @tfcall(:TF_SetAttrFloatList, Void, (Ptr{Void}, Cstring, Ptr{Float32}, Cint), desc.ptr, attr_name, list, length(list)) + @tfcall(:TF_SetAttrFloatList, Cvoid, (Ptr{Cvoid}, Cstring, Ptr{Float32}, Cint), desc.ptr, attr_name, list, length(list)) end function set_attr_list(desc::NodeDescription, attr_name, list::Vector{<:DataType}) list = map(jl_to_df_type, list) - @tfcall(:TF_SetAttrTypeList, Void, (Ptr{Void}, Cstring, Ptr{Void}, Cint), desc.ptr, attr_name, list, length(list)) + @tfcall(:TF_SetAttrTypeList, Cvoid, (Ptr{Cvoid}, Cstring, Ptr{Cvoid}, Cint), desc.ptr, attr_name, list, length(list)) end function set_attr_shape_list(desc::NodeDescription, attr_name, list::Vector) @@ -1329,7 +1325,7 @@ function set_attr_shape_list(desc::NodeDescription, attr_name, list::Vector) for shape in list push!(dims, [shape...]) end - @tfcall(:TF_SetAttrShapeList, Void, (Ptr{Void}, Cstring, Ptr{Ptr{Int64}}, Ptr{Cint}, Cint), + @tfcall(:TF_SetAttrShapeList, Cvoid, (Ptr{Cvoid}, Cstring, Ptr{Ptr{Int64}}, Ptr{Cint}, Cint), desc.ptr, attr_name, dims, @@ -1338,14 +1334,14 @@ function set_attr_shape_list(desc::NodeDescription, attr_name, list::Vector) end function Base.eltype(t::RawTensor) - tf_type = @tfcall(:TF_TensorType, TF_DataType, (Ptr{Void},), t.ptr) + tf_type = @tfcall(:TF_TensorType, TF_DataType, (Ptr{Cvoid},), t.ptr) tf_to_jl_type(tf_type) end const type_map = Dict(TF_UINT8=>UInt8, TF_FLOAT=>Float32, TF_INT32=>Int32, TF_INT64=>Int64, TF_DOUBLE=>Float64, TF_STRING=>String, - TF_BOOL=>Bool, TF_COMPLEX64=>Complex64, - TF_COMPLEX128=>Complex128) + TF_BOOL=>Bool, TF_COMPLEX64=>ComplexF32, + TF_COMPLEX128=>ComplexF64) const inv_type_map = Dict(v=>k for (k, v) in type_map) function tf_to_jl_type(dt::TF_DataType) @@ -1358,7 +1354,7 @@ end function Base.convert(::Type{Array}, t::RawTensor) dims = ndims(t) - data = @tfcall(:TF_TensorData, Ptr{Void}, (Ptr{Void},), t.ptr) + data = @tfcall(:TF_TensorData, Ptr{Cvoid}, (Ptr{Cvoid},), t.ptr) data = convert(Ptr{eltype(t)}, data) if eltype(t) == String d = size(t) @@ -1366,10 +1362,10 @@ function Base.convert(::Type{Array}, t::RawTensor) array = unsafe_wrap(Array, convert(Ptr{UInt8}, data), sizeof(t)) b = IOBuffer(array) seekstart(b) - read(b, UInt64, prod(d)) # The offsets + read(b, sizeof(Int64)*prod(d)) # The offsets for i in 1:prod(d) len = varint_decode(b) - raw_data = read(b, UInt8, len) + raw_data = read(b, len) push!(out, String(raw_data)) end out @@ -1390,7 +1386,7 @@ end function get_proto(graph::Graph) output = Buffer() status = Status() - @tfcall(:TF_GraphToGraphDef, Void, (Ptr{Void}, Ptr{Void}, Ptr{Void}), graph.ptr, output.ptr, status.ptr) + @tfcall(:TF_GraphToGraphDef, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}), graph.ptr, output.ptr, status.ptr) check_status(status) convert(Array, output) end @@ -1398,7 +1394,7 @@ end function get_proto(node::Operation) output = Buffer() status = Status() - @tfcall(:TF_OperationToNodeDef, Void, (Ptr{Void}, Ptr{Void}, Ptr{Void}), node.ptr, output.ptr, status.ptr) + @tfcall(:TF_OperationToNodeDef, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}), node.ptr, output.ptr, status.ptr) check_status(status) convert(Array, output) end @@ -1448,7 +1444,7 @@ Returns an operation by searching for its name in the given graph. """ @with_def_graph function get_node_by_name(graph::Graph, name::AbstractString) name, port = parse_port_name(name) - node_ptr = @tfcall(:TF_GraphOperationByName, Ptr{Void}, (Ptr{Void}, Cstring), graph.ptr, name) + node_ptr = @tfcall(:TF_GraphOperationByName, Ptr{Cvoid}, (Ptr{Cvoid}, Cstring), graph.ptr, name) if node_ptr == C_NULL return Nullable{Operation}() else @@ -1479,7 +1475,7 @@ Base.haskey(graph::Graph, name) = !isnull(get_node_by_name(graph, name)) -node_name(::Void) = nothing +node_name(::Cvoid) = nothing node_name(xs::AbstractVector)=node_name.(xs) """ @@ -1492,8 +1488,8 @@ ys and xs are each a Tensor or a list of tensors. grad_ys is a list of Tensor, h gradients() adds ops to the graph to output the partial derivatives of ys with respect to xs. It returns a list of Tensor of length len(xs) where each tensor is the sum(dy/dx) for y in ys. `grad_ys` is a tensor or list of tensors which holds the initial gradients for each y in ys. -If `ys` is a single tensor `grad_ys` must be a single tensor; if `ys` is a list of tensors then likewise `grad_ys` must be a list of the smae size. -When `grad_ys` is `nothing`, it is effectiely defaulted to a tensor of '1's of the shape of y for each y in ys. +If `ys` is a single tensor `grad_ys` must be a single tensor; if `ys` is a list of tensors then likewise `grad_ys` must be a list of the same size. +When `grad_ys` is `nothing`, it is effectively defaulted to a tensor of '1's of the shape of y for each y in ys. `grad_ys` can be partialy specified, with some gradients given and others left to default, py passing their values as `nothing`. A user can provide their own initial `grad_ys` to compute the derivatives using a different initial gradient for each y (e.g., if one wanted to weight the gradient differently for each value in each y). @@ -1519,7 +1515,7 @@ function add_gradients_py(y, x::AbstractArray, grad_y=nothing) for name in grad_names if isa(name, String) push!(out, get_tensor_by_name(name)) - elseif isa(name, Void) + elseif isa(name, Cvoid) push!(out, nothing) else push!(out, IndexedSlices(get_tensor_by_name(name[1]), get_tensor_by_name(name[2])+1)) @@ -1533,13 +1529,13 @@ function add_gradients_c(y::AbstractArray, x::AbstractArray, dx=nothing) status = Status() graph = get_def_graph() grads = Array{Port}(length(x)) - if dx isa AbstractArray{Void} || dx isa Void + if dx isa AbstractArray{Cvoid} || dx isa Cvoid c_dx = C_NULL else c_dx = Port.(Tensor.(dx)) end - @tfcall(:TF_AddGradients, Void, - (Ptr{Void}, Ptr{Void}, Cint, Ptr{Void}, Cint, Ptr{Void}, Ptr{Void}, Ptr{Void}), + @tfcall(:TF_AddGradients, Cvoid, + (Ptr{Cvoid}, Ptr{Cvoid}, Cint, Ptr{Cvoid}, Cint, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}), graph.ptr, Port.(y), length(y), Port.(x), length(x), c_dx, status.ptr, grads ) check_status(status) @@ -1549,17 +1545,17 @@ end function get_num_outputs(op::Operation) - @tfcall(:TF_OperationNumOutputs, Cint, (Ptr{Void},), op.ptr) |> Int + @tfcall(:TF_OperationNumOutputs, Cint, (Ptr{Cvoid},), op.ptr) |> Int end function get_device(op::Operation) - @tfcall(:TF_OperationDevice, Cstring, (Ptr{Void},), op.ptr) |> String + @tfcall(:TF_OperationDevice, Cstring, (Ptr{Cvoid},), op.ptr) |> String end get_device(t::Tensor) = get_device(t.op) function num_outputs(op::Operation) - @tfcall(:TF_OperationNumOutputs, Cint, (Ptr{Void},), op.ptr) |> Int + @tfcall(:TF_OperationNumOutputs, Cint, (Ptr{Cvoid},), op.ptr) |> Int end get_op(op::Operation) = op @@ -1587,43 +1583,43 @@ end @with_def_graph function import_graph_def(graph::Graph, graph_def::Vector{UInt8}, options=GraphImportOptions()) version_check(v"1.0.0-rc1") - options_ptr = @tfcall(:TF_NewImportGraphDefOptions, Ptr{Void}, ()) + options_ptr = @tfcall(:TF_NewImportGraphDefOptions, Ptr{Cvoid}, ()) ## BEGIN SET OPTIONS for ((input_name, input_port), tensor) in options.input_mapping - @tfcall(:TF_ImportGraphDefOptionsAddInputMapping, Void, - (Ptr{Void}, Cstring, Cint, Port), + @tfcall(:TF_ImportGraphDefOptionsAddInputMapping, Cvoid, + (Ptr{Cvoid}, Cstring, Cint, Port), options_ptr, input_name, input_port-1, Port(tensor)) end - + for (output_name, output_port) in options.return_output - @tfcall(:TF_ImportGraphDefOptionsAddReturnOutput, Void, - (Ptr{Void}, Cstring, Cint), + @tfcall(:TF_ImportGraphDefOptionsAddReturnOutput, Cvoid, + (Ptr{Cvoid}, Cstring, Cint), options_ptr, output_name, output_port-1) end - + for operation in options.control_dependencies - @tfcall(:TF_ImportGraphDefOptionsAddControlDependency, Void, - (Ptr{Void}, Ptr{Void}), + @tfcall(:TF_ImportGraphDefOptionsAddControlDependency, Cvoid, + (Ptr{Cvoid}, Ptr{Cvoid}), options_ptr, operation.ptr) end - @tfcall(:TF_ImportGraphDefOptionsSetPrefix, Void, - (Ptr{Void}, Cstring), + @tfcall(:TF_ImportGraphDefOptionsSetPrefix, Cvoid, + (Ptr{Cvoid}, Cstring), options_ptr, options.prefix) ## END SET OPTIONS status = Status() buffer = Buffer(graph_def) - output_ports = Vector{Port}(length(options.return_output)) - - @tfcall(:TF_GraphImportGraphDefWithReturnOutputs, Void, - (Ptr{Void}, Ptr{Void}, Ptr{Void}, Ptr{Void}, Cint, Ptr{Void}), + output_ports = Vector{Port}(undef, length(options.return_output)) + + @tfcall(:TF_GraphImportGraphDefWithReturnOutputs, Cvoid, + (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Cint, Ptr{Cvoid}), graph.ptr, buffer.ptr, options_ptr, output_ports, length(output_ports), status.ptr) - + check_status(status) - - @tfcall(:TF_DeleteImportGraphDefOptions, Void, (Ptr{Void},), options_ptr) + + @tfcall(:TF_DeleteImportGraphDefOptions, Cvoid, (Ptr{Cvoid},), options_ptr) output_tensors = map(Tensor, output_ports) output_tensors end @@ -1644,15 +1640,20 @@ struct OperationIteratorState pos::Int end -function Base.start(iter::OperationIterator) - state = OperationIteratorState(Nullable{Operation}(), 0) - _next(iter, state)[2] +#function Base.start(iter::OperationIterator) +# state = OperationIteratorState(Nullable{Operation}(), 0) +# _next(iter, state)[2] +#end + +function Base.iterate(iter::OperationIterator, state=_next(iter, OperationIteratorState(Nullable{Operation}(), 0))[2]) + value, new_state = _next(iter, state) + isnull(state.next_op) ? nothing : (get(value), new_state) end function _next(iter::OperationIterator, state) pos = Ref{Csize_t}(state.pos) - op_ptr = @tfcall(:TF_GraphNextOperation, Ptr{Void}, - (Ptr{Void}, Ref{Csize_t}), + op_ptr = @tfcall(:TF_GraphNextOperation, Ptr{Cvoid}, + (Ptr{Cvoid}, Ref{Csize_t}), iter.graph.ptr, pos) if op_ptr == C_NULL next_op = Nullable{Operation}() @@ -1666,14 +1667,14 @@ function _next(iter::OperationIterator, state) (state.next_op, next_state) end -function Base.next(iter::OperationIterator, state) - value, new_state = _next(iter, state) - (get(value), new_state) -end +#function Base.next(iter::OperationIterator, state) +# value, new_state = _next(iter, state) +# (get(value), new_state) +#end -Base.done(iter::OperationIterator, state) = isnull(state.next_op) +#Base.done(iter::OperationIterator, state) = isnull(state.next_op) -Base.iteratorsize(::Type{OperationIterator}) = Base.SizeUnknown() +Base.IteratorSize(::Type{OperationIterator}) = Base.SizeUnknown() Base.eltype(::Type{OperationIterator}) = Operation @with_def_graph function get_operations(g::Graph) @@ -1688,7 +1689,7 @@ const op_list = Dict{String, tensorflow.OpDef}() function get_all_op_list() !isempty(op_list) && return op_list - buf_ptr = @tfcall(:TF_GetAllOpList, Ptr{Void}, ()) + buf_ptr = @tfcall(:TF_GetAllOpList, Ptr{Cvoid}, ()) buffer = Buffer(buf_ptr) data = convert(Array, buffer) b = IOBuffer(data) diff --git a/src/generate_ops.jl b/src/generate_ops.jl index c534b11a..0020a48f 100644 --- a/src/generate_ops.jl +++ b/src/generate_ops.jl @@ -44,11 +44,11 @@ function opname_to_jlname(name) next_char = name[idx+1] if idx < length(name)-1 next_next_char = name[idx+2] - if isupper(cur_char) && isupper(next_char) && islower(next_next_char) + if isuppercase(cur_char) && isuppercase(next_char) && islowercase(next_next_char) word_end = true end end - if islower(cur_char) && isupper(next_char) + if islowercase(cur_char) && isuppercase(next_char) word_end = true end end @@ -69,7 +69,7 @@ Returns `true` if the given operation attribute is not meant to be supplied by the user and `false` otherwise. """ function is_internal_arg(arg) - arg._type == "type" && ismatch(r"^T", arg.name) + arg._type == "type" && occursin(r"^T", arg.name) end function to_function(op::tensorflow.OpDef) @@ -174,7 +174,7 @@ function to_function(op::tensorflow.OpDef) end end) end - unshift!(inputs, kwargs) + pushfirst!(inputs, kwargs) scalar_output = true if length(op.output_arg) > 1 scalar_output = false @@ -259,7 +259,7 @@ stringify_func(op::tensorflow.OpDef) = stringify_func(to_function(op)) function load_default_imports() path = joinpath(@__DIR__, "../deps/default_imports.txt") names = readlines(path) - filtered = [name for name in names if !ismatch(r"^#", name)] + filtered = [name for name in names if !occursin(r"^#", name)] return filtered end @@ -289,7 +289,7 @@ function import_ops(op_names) print(ops_file, "\n\n") catch err err_msg = sprint(showerror, err) - warn("Could not import operation $name: $err_msg") + @warn("Could not import operation $name: $err_msg") end end write(ops_file, """ @@ -330,13 +330,13 @@ Returns a reference to a Julia function corresponding to the operation. function import_op(name) jl_name = opname_to_jlname(name) mod = TensorFlow.Ops - if jl_name ∉ names(mod, true) + if jl_name ∉ names(mod, all=true) ops = Dict(get_all_op_list()) op = ops[name] op_desc = to_function(op) - eval(Ops, op_desc.expr) + Core.eval(Ops, op_desc.expr) else - warn("Import Skipped: tried to import op $name as $(mod).$(jl_name), but that already exists.") + @warn("Import Skipped: tried to import op $name as $(mod).$(jl_name), but that already exists.") end return getfield(Ops, jl_name) diff --git a/src/io/tfrecord.jl b/src/io/tfrecord.jl index 50f77f5f..7ee910f1 100644 --- a/src/io/tfrecord.jl +++ b/src/io/tfrecord.jl @@ -5,11 +5,14 @@ RecordWriter, RecordIterator import TensorFlow +import Distributed const tf = TensorFlow + +using Nullables using PyCall struct RecordWriter - pyo::Future + pyo::Distributed.Future end """ @@ -47,11 +50,7 @@ function Base.close(writer::RecordWriter) end struct RecordIterator - pyo::Future -end - -struct RecordIteratorState - val::Nullable{Vector{UInt8}} + pyo::Distributed.Future end """ @@ -65,42 +64,52 @@ function RecordIterator(path::AbstractString) RecordIterator(pyo) end - - function _next(iter::RecordIterator) try - @static if PyCall.pyversion >= v"3.0.0" - record = fetch(@tf.py_proc $(iter.pyo)[:__next__]()) + ans=@static if PyCall.pyversion >= v"3.0.0" + fetch(@tf.py_proc $(iter.pyo)[:__next__]()) else #Python 2 - record = fetch(@tf.py_proc $(iter.pyo)[:next]()) + fetch(@tf.py_proc $(iter.pyo)[:next]()) end - RecordIteratorState(Nullable(record)) + Vector{UInt8}(ans) catch err - if isa(err, RemoteException) && isa(err.captured.ex, PyCall.PyError) + if isa(err, Distributed.RemoteException) && isa(err.captured.ex, PyCall.PyError) # Only catch it, if it could be an StopIteration exception thrown in python # which signifies the end of iteration being reached normally - RecordIteratorState(Nullable()) + nothing # signal to stop julia iteration else rethrow(err) end end end -function Base.start(iter::RecordIterator) - return _next(iter) +function Base.iterate(iter::RecordIterator, state=iter) + record = _next(iter) + if record isa Nothing + nothing # end iteration + else + (record, iter) + end end -function Base.next(iter::RecordIterator, state) - val = get(state.val) - return val, _next(iter) -end +#function Base.start(iter::RecordIterator) +# return _next(iter) +#end -function Base.done(iter::RecordIterator, state) - isnull(state.val) -end +#function Base.next(iter::RecordIterator, state) +# val = get(state.val) +# return val, _next(iter) +#end + +#function Base.done(iter::RecordIterator, state) +# isnull(state.val) +#end + +#Base.IteratorSize(::RecordIterator) = Base.SizeUnknown() +#Base.IteratorEltype(::RecordIterator) = Vector{UInt8} -Base.iteratorsize(::Type{RecordIterator}) = Base.SizeUnknown() -Base.eltype(::Type{RecordIterator}) = Vector{UInt8} +Base.IteratorSize(::RecordIterator) = Base.SizeUnknown() +Base.eltype(::RecordIterator) = Vector{UInt8} end diff --git a/src/meta.jl b/src/meta.jl index 19c890e9..dcb491db 100644 --- a/src/meta.jl +++ b/src/meta.jl @@ -15,7 +15,6 @@ const registered_ops = Set() macro op(f) # TODO: fix MacroTools to make this work again - # f = longdef(f) #convert to long form function opname = @match f begin function opname_(args__) body_ @@ -27,10 +26,10 @@ macro op(f) end end opname === nothing && error("Invalid usage of @op") - + already_registered = opname ∈ registered_ops push!(registered_ops, opname) - @assert(isdefined(:tf)) # Need tf as name for module where this code is located + @assert(@isdefined(tf)) # Need tf as name for module where this code is located if already_registered register_block = nothing else @@ -41,7 +40,7 @@ macro op(f) elseif isa($(opname), DataType) tf.is_registered_op(::Type{$(opname)}) = tf.RegisteredOp() else - warn("@op used on " * string($(opname)) * " which does not seem to be a suitable type for an operation.") + @warn("@op used on " * string($(opname)) * " which does not seem to be a suitable type for an operation.") end end end @@ -74,8 +73,8 @@ withname(f::F, name) where F<:Function = withname(is_registered_op(F), f, name) withname(::NotRegisteredOp, f, name) = (args...; kws...) -> f(args...; kws...) withname(::RegisteredOp, f, name) = (args...; kws...) -> begin - if !any(first.(kws) .== :name) # name is not already there - push!(kws, (:name, name)) + if !any(keys(kws) .== :name) # name is not already there + kws = (kws..., name=name) end f(args...; kws...) end diff --git a/src/ops.jl b/src/ops.jl index e796b70e..5bda6d07 100644 --- a/src/ops.jl +++ b/src/ops.jl @@ -2,7 +2,6 @@ using MacroTools import Base: log, exp, +, -, *, /, ^, sin, cos, tan, asin, acos, atan, div, tanh, sqrt, abs, floor, ceil, floor, sign import TensorFlow const tf = TensorFlow # so know where op_funcs is defined -import TakingBroadcastSeriously: @unfuse, broadcast_ """ tf_promote(args...) @@ -27,7 +26,7 @@ function tf_promote(args...) push!(new_args, convert(Tensor{big_type}, arg)) end end - (new_args...) + (new_args...,) end macro define_binary(jl_func, tf_func) @@ -46,28 +45,26 @@ macro define_unary(jl_func, tf_func) end |> esc end -@unfuse AbstractTensor macro define_broadcast(jl_op, tf_func) quote - broadcast_(::typeof($jl_op), t1::AbstractTensor, t2::AbstractTensor) = $tf_func(tf_promote(t1, t2)...) - broadcast_(::typeof($jl_op), t1::AbstractTensor, t2) = $tf_func(t1, Tensor(t2)) - broadcast_(::typeof($jl_op), t1, t2::AbstractTensor) = $tf_func(Tensor(t1), t2) + Base.Broadcast.broadcasted(::typeof($jl_op), t1::AbstractTensor, t2::AbstractTensor) = $tf_func(tf_promote(t1, t2)...) + Base.Broadcast.broadcasted(::typeof($jl_op), t1::AbstractTensor, t2) = $tf_func(t1, Tensor(t2)) + Base.Broadcast.broadcasted(::typeof($jl_op), t1, t2::AbstractTensor) = $tf_func(Tensor(t1), t2) + Base.Broadcast.broadcasted(::typeof($jl_op), t1::AbstractTensor, t2::Base.Broadcast.Broadcasted) = $tf_func(t1, Tensor(collect(t2))) + Base.Broadcast.broadcasted(::typeof($jl_op), t1::Base.Broadcast.Broadcasted, t2::AbstractTensor) = $tf_func(Tensor(collect(t1)), t2) end |> esc end -macro not_implemented(f) - res = @match f begin - function name_(args__) - body__ - end => name, args - end - res === nothing && error("Invalid use of not_implemented") - func_name, args = res - quote - function $(esc(func_name))(args...) - error("Not implemented yet") +macro not_implemented(ff) + if @capture(ff, function name_(args__) body__ end) + quote + function $(esc(name))(args...) + error("Not implemented yet") + end end + else + error("Invalid use of not_implemented") end end diff --git a/src/ops/comparison.jl b/src/ops/comparison.jl index 6c0d348f..704c4a01 100644 --- a/src/ops/comparison.jl +++ b/src/ops/comparison.jl @@ -1,8 +1,8 @@ -import Base: less +#import InteractiveUtils #less import .Ops: equal, not_equal, less_equal, greater, greater_equal, where -Base.less(x::AbstractTensor, y::AbstractTensor; kwargs...) = Ops.less(x, y; kwargs...) +less(x::AbstractTensor, y::AbstractTensor; kwargs...) = Ops.less(x, y; kwargs...) const func_list = [ (:less, :<), @@ -22,6 +22,8 @@ for (func, sym) in func_list @eval @define_broadcast($sym, $func) end -Base.select(condition::AbstractTensor, args...; kwargs...) = Ops.select(condition, args...; kwargs...) +function select(condition::AbstractTensor, args...; kwargs...) + Ops.select(condition, args...; kwargs...) +end -Base.find(input::AbstractTensor) = Ops.where(input)+1 # Convert from 0-based indices +Base.findall(input::AbstractTensor) = Ops.where(input)+1 # Convert from 0-based indices diff --git a/src/ops/control_flow.jl b/src/ops/control_flow.jl index e136390c..d9400024 100644 --- a/src/ops/control_flow.jl +++ b/src/ops/control_flow.jl @@ -1,4 +1,5 @@ using Compat +import LinearAlgebra # cond import .Ops: no_op, count_up_to @@ -103,7 +104,7 @@ Example: # Operations in f2 (e.g., tf.add) are not executed. ``` """ -@op function Base.cond(pred::AbstractTensor, fn1, fn2; name=nothing) +@op function LinearAlgebra.cond(pred::AbstractTensor, fn1, fn2; name=nothing) # TODO add control dependencies to subgraphs local merge with_op_name(name, "cond") do diff --git a/src/ops/imported_ops.jl b/src/ops/imported_ops.jl index 32649248..14b582ef 100644 --- a/src/ops/imported_ops.jl +++ b/src/ops/imported_ops.jl @@ -2,6 +2,7 @@ module Ops import TensorFlow +import SpecialFunctions const tf = TensorFlow """ equal(x, y) diff --git a/src/ops/indexing.jl b/src/ops/indexing.jl index 995fefaa..0a8410d0 100644 --- a/src/ops/indexing.jl +++ b/src/ops/indexing.jl @@ -21,8 +21,9 @@ This matchs python TensorFlow `size` operation https://www.tensorflow.org/versions/r0.10/api_docs/python/array_ops.html#size """ @define_unary Base.length Ops.size -@define_unary Base.endof length +@define_unary Base.lastindex length +Base.lastindex(x::AbstractTensor, dim) = size(x, dim) struct TensorRange start::Tensor{Int32} @@ -31,9 +32,9 @@ end Base.first(tr::TensorRange)=tr.start Base.last(tr::TensorRange)=tr.stop -@define_binary Base.colon TensorRange +@define_binary Base.:(:) TensorRange + - const Slice = Union{TensorRange, UnitRange, Colon} const Index = Union{<:Integer, AbstractArray{<:Integer}, AbstractTensor{<:Integer}} @@ -71,7 +72,7 @@ function Base.getindex(params::AbstractTensor, ind1::Union{Slice, Index}, inds: end function proc_ind!(ind::Union{UnitRange, TensorRange}) - #NOTE: end has (during code lowering) been replace with `endof(X)` or `size(X,d)` giving the actual size + #NOTE: end has (during code lowering) been replace with `lastindex(X)` or `size(X,d)` giving the actual size begin_ = first(ind) push!(begins, begin_) end_ = last(ind) @@ -105,7 +106,7 @@ function Base.getindex(params::AbstractTensor, ind1::Union{Slice, Index}, inds: sizes_tensor = stack(sizes) sliced = slice(params, begins_tensor, sizes_tensor) if length(singleton_dims)>0 # we are not slicing on all axies - squeeze(sliced, singleton_dims) # Squeeze singleton indexes + dropdims(sliced, dims=singleton_dims) # Drop singleton indexes else # we are slicing on all axies sliced end diff --git a/src/ops/init_ops.jl b/src/ops/init_ops.jl index b72bf530..3c0caf69 100644 --- a/src/ops/init_ops.jl +++ b/src/ops/init_ops.jl @@ -6,13 +6,23 @@ function Base.rand(c::ConstantInitializer, shape...) fill(c.value, shape) end +function Base.rand(c::ConstantInitializer, shape::Integer...) + fill(c.value, shape) +end + +#function rand(c::ConstantInitializer, shape::Integer, shapes::Integer...) +# fill(c.value, [shape, shapes...]...) +#end + """ zeros_initializer(dtype=Float32) Initializer that generates tensors initialized to 0. """ function zeros_initializer(dtype=Float32) - size->zeros(Tensor{dtype}, size...) + function(size::Array{T, 1} where T<:Integer) + zeros(Tensor{dtype}, size...) + end end """ @@ -21,5 +31,7 @@ end Initializer that generates tensors initialized to 1. """ function ones_initializer(dtype=Float32) - size->ones(Tensor{dtype}, size...) + function(size::Array{T, 1} where T<:Integer) + ones(Tensor{dtype}, size...) + end end diff --git a/src/ops/math.jl b/src/ops/math.jl index 97a96a68..c605280c 100644 --- a/src/ops/math.jl +++ b/src/ops/math.jl @@ -1,3 +1,7 @@ +import LinearAlgebra +import SpecialFunctions +import Statistics + import .Ops: add_n, arg_min, @@ -23,24 +27,22 @@ import .Ops: segment_prod -@op Base.indmin(n::AbstractTensor, dim; name=nothing) = Ops.arg_min(n, dim; name=name)+1 +@op Base.argmin(n::AbstractTensor, dim; name=nothing) = Ops.arg_min(n, dim; name=name)+1 -@op Base.indmax(n::AbstractTensor, dim; name=nothing) = Ops.arg_max(n, dim; name=name)+1 +@op Base.argmax(n::AbstractTensor, dim; name=nothing) = Ops.arg_max(n, dim; name=name)+1 @op Base.max(x::AbstractTensor, y; kwargs...) = Ops.maximum(x, y; kwargs...) @op Base.min(x::AbstractTensor, y; kwargs...) = Ops.minimum(x, y; kwargs...) -@op function Base.svd(a::AbstractTensor; thin=true, kwargs...) +@op function LinearAlgebra.svd(a::AbstractTensor; full=false, kwargs...) # Match Base names and ordering of results - s,u,v = Ops.svd(a; compute_uv=true, full_matrices=!thin, kwargs...) + s,u,v = Ops.svd(a; compute_uv=true, full_matrices=full, kwargs...) u,s,v end -# const multiply = Ops.mul -# const negative = Ops.neg @define_unary negative Ops.neg @define_binary multiply Ops.mul const self_adjoint_eig = Ops.self_adjoint_eig_v2 @@ -64,6 +66,8 @@ const matmul = mat_mul @define_broadcast(/, Ops.div) @define_broadcast(^, pow) +Broadcast.broadcasted(::typeof(Base.literal_pow), ::typeof(^), x::AbstractTensor, y::Val{T}) where T = x^Tensor(T) + @op function batch_matmul(x::AbstractTensor,y::AbstractTensor; adj_x=false, adj_y=false, name=nothing) if tf_version() >= v"1.0.0-" Base.depwarn(""" @@ -111,7 +115,7 @@ Returns: Tensor(Operation(desc), 1) end -@op function Base.cross(n1::AbstractTensor, n2::AbstractTensor; kwargs...) +@op function LinearAlgebra.cross(n1::AbstractTensor, n2::AbstractTensor; kwargs...) Ops.cross(n1, n2; kwargs...) end @@ -133,9 +137,6 @@ for jl_func_name in [ :asin, :acos, :tanh, - :lgamma, - :erf, - :erfc, :real, :imag, :sign, @@ -144,16 +145,25 @@ for jl_func_name in [ @eval @define_unary Base.$jl_func_name Ops.$jl_func_name end -function Base.round(::Type{T}, value::AbstractTensor) where T - convert(Tensor{T}, round(value)) + +for jl_func_name in [ + :lgamma, + :erf, + :erfc] + @eval @define_unary SpecialFunctions.$jl_func_name Ops.$jl_func_name end for jl_func_name in [ :polygamma, :zeta] - @eval @define_binary Base.$jl_func_name Ops.$jl_func_name + @eval @define_binary SpecialFunctions.$jl_func_name Ops.$jl_func_name end +function Base.round(::Type{T}, value::AbstractTensor) where T + convert(Tensor{T}, round(value)) +end + + -(n::AbstractTensor) = negative(n) @op function Base.complex(x_r::AbstractTensor, x_i::AbstractTensor; kwargs...) @@ -162,13 +172,20 @@ end # Matrix math +@define_unary Base.inv Ops.matrix_inverse + for (jl_func_name, tf_func_name) in [ - (:inv, :matrix_inverse), (:det, :matrix_determinant), - (:diagm, :diag), (:diag, :matrix_diag_part)] - @eval @define_unary Base.$jl_func_name Ops.$tf_func_name + @eval @define_unary LinearAlgebra.$jl_func_name Ops.$tf_func_name +end + +function LinearAlgebra.diagm(kv::Pair{T, S}) where {T<:Integer, S<:AbstractTensor} + if kv.first == 0 + return Ops.diag(kv.second) + end + error("diagm only supports the calling form diagm(0=>x) where 'x' is a tensor.") end # Reductions @@ -211,15 +228,15 @@ end # TODO Match Julia reduction behavior when `axis` is passed for (jl_func, tf_func) in [ - (:sum, :reduce_sum), - (:prod, :reduce_prod), - (:minimum, :reduce_min), - (:maximum, :reduce_max), - (:all, :reduce_all), - (:any, :reduce_any), - (:mean, :reduce_mean), + (:(Base.sum), :reduce_sum), + (:(Base.prod), :reduce_prod), + (:(Base.minimum), :reduce_min), + (:(Base.maximum), :reduce_max), + (:(Base.all), :reduce_all), + (:(Base.any), :reduce_any), + (:(Statistics.mean), :reduce_mean), ] - @eval function Base.$jl_func(n::AbstractTensor, axis=nothing; kwargs...) + @eval function $jl_func(n::AbstractTensor, axis=nothing; kwargs...) $tf_func(n; axis=axis, kwargs...) end end diff --git a/src/ops/rnn_cell.jl b/src/ops/rnn_cell.jl index 9e7c55cf..46c73096 100644 --- a/src/ops/rnn_cell.jl +++ b/src/ops/rnn_cell.jl @@ -11,6 +11,7 @@ RNNCell, IdentityRNNCell using Compat +using Nullables import ..nn import .nn: TensorFlow import .TensorFlow: Operation, get_shape, get_variable, select, tanh, Tensor, nn, concat, expand_dims, with_op_name, AbstractTensor diff --git a/src/ops/sequences.jl b/src/ops/sequences.jl index 4f1da69d..6633ab6b 100644 --- a/src/ops/sequences.jl +++ b/src/ops/sequences.jl @@ -3,6 +3,8 @@ import .Ops: random_standard_normal, random_shuffle +import Random # shuffle + function convert_eltype(x::Array, dtype) convert(Array{dtype}, x) end @@ -27,10 +29,10 @@ convert_eltype(x, dtype) = x end for f in [:zeros, :ones] - @eval Base.$f(::Type{Tensor}, args...) = $f(Tensor{Float32}, args...) - @eval Base.$f(::Type{Tensor}, args::Tuple) = $f(Tensor, args...) - @eval Base.$f(::Type{Tensor{T}}, args...) where {T} = constant($f(T, args...)) - @eval Base.$f(::Type{Tensor{T}}, args::Tuple) where {T} = constant($f(T, args)) + @eval Base.$f(::Type{Tensor}, args::Integer...) = $f(Tensor{Float32}, args...) + @eval Base.$f(::Type{Tensor}, args::NTuple{N, Integer}) where N = $f(Tensor, args...) + @eval Base.$f(::Type{Tensor{T}}, args::Integer...) where {T} = constant($f(T, args...)) + @eval Base.$f(::Type{Tensor{T}}, args::NTuple{N, Integer}) where {T, N} = constant($f(T, args)) end @op function random_normal(shape; mean=0.0, stddev=1.0, dtype=Float32, name=nothing, kwargs...) @@ -76,11 +78,11 @@ A `Tensor` of the specified `shape` and `dtype` containing random values. end -@op function Base.shuffle(t::AbstractTensor; kwargs...) +@op function Random.shuffle(t::AbstractTensor; kwargs...) Ops.random_shuffle(t; kwargs...) end -@op function Base.linspace(start::AbstractTensor, stop, num; kwargs...) +@op function Base.range(start::AbstractTensor; stop, num=Union{Integer, Nothin}, kwargs...) Ops.lin_space(start, stop, num; kwargs...) end diff --git a/src/ops/transformations.jl b/src/ops/transformations.jl index e0cbbaad..27974f0e 100644 --- a/src/ops/transformations.jl +++ b/src/ops/transformations.jl @@ -8,7 +8,7 @@ import .Ops: scatter_nd, dynamic_partition, dynamic_stitch - +import LinearAlgebra const concat = Ops.concat_v2 const stack = Ops.pack @@ -57,7 +57,7 @@ tf.shape(split0) ==> [5, 10] Note: If you are splitting along an axis by the length of that axis, consider using unpack, e.g. num_items = t.get_shape()[axis].value -[tf.squeeze(s, [axis]) for s in tf.split(axis, num_items, t)] +[tf.dropdims(s, dims=[axis]) for s in tf.split(axis, num_items, t)] can be rewritten as tf.unpack(t, axis=axis) @@ -197,7 +197,7 @@ end """ - squeeze(x::AbstractTensor, squeeze_dims; name="squeeze") + dropdims(x::AbstractTensor, dims; name="squeeze") Removes dimensions of size 1 from the shape of a tensor. Given a tensor `input`, this operation returns a tensor of the same type with @@ -207,37 +207,37 @@ dimensions, you can remove specific size 1 dimensions by specifying For example: ```prettyprint # 't' is a tensor of shape [1, 2, 1, 3, 1, 1] -shape(squeeze(t)) ==> [2, 3] +shape(dropdims(t)) ==> [2, 3] ``` Or, to remove specific size 1 dimensions: ```prettyprint # 't' is a tensor of shape [1, 2, 1, 3, 1, 1] -shape(squeeze(t, [3, 5])) ==> [1, 2, 3, 1] +shape(dropdims(t; dims=[3, 5])) ==> [1, 2, 3, 1] ``` Args: - input: A `Tensor`. The `input` to squeeze. + input: A `Tensor`. The `input` to dropdims. axis: An optional list of `ints`. Defaults to `[]`. If specified, only squeezes the dimensions listed. The dimension index starts at 1. It is an error to squeeze a dimension that is not 1. name: A name for the operation (optional). - squeeze_dims: Deprecated keyword argument that is now axis. + dims: Deprecated keyword argument that is now axis. Returns: A `Tensor`. Has the same type as `input`. Contains the same data as `input`, but has one or more dimensions of size 1 removed. Raises: - ValueError: When both `squeeze_dims` and `axis` are specified. + ValueError: When both `dims` and `axis` are specified. """ -@op function Base.squeeze(x::AbstractTensor, squeeze_dims=nothing; kwargs...) - if squeeze_dims !== nothing - squeeze_dims = squeeze_dims - 1 +@op function Base.dropdims(x::AbstractTensor; dims=nothing, kwargs...) + if dims !== nothing + dims = dims .- 1 end - Ops.squeeze(x; squeeze_dims=squeeze_dims, kwargs...) + Ops.squeeze(x; squeeze_dims=dims, kwargs...) end """ -Base.rank(n::AbstractTensor; name="") +LinearAlgebra.rank(n::AbstractTensor; name="") Returns the rank of a tensor. @@ -260,7 +260,7 @@ A Tensor of type int32. https://www.tensorflow.org/versions/r0.10/api_docs/python/array_ops.html#rank """ -@define_unary Base.rank Ops.rank +@define_unary LinearAlgebra.rank Ops.rank @op function scatter_nd(indices, updates, shape::TensorFlow.TensorShape; name=nothing) if shape.rank_unknown || any(isnull.(shape.dims)) @@ -311,8 +311,8 @@ boolean_mask(tensor, mask) ==> [[1, 2], [5, 6]] @op function boolean_mask(tensor, mask::AbstractTensor; name=nothing) local result with_op_name(name, "BooleanMask") do - indices = find(mask) # TODO generalize to more dimensions - squeezed = squeeze(indices, [2]) + indices = findall(mask) # TODO generalize to more dimensions + squeezed = dropdims(indices, dims=[2]) result = tensor[squeezed] end result @@ -321,7 +321,7 @@ end @op function boolean_mask(tensor, mask::AbstractArray; name=nothing) local result with_op_name(name, "BooleanMask") do - indices = find(mask) # TODO generalize to more dimensions + indices = findall(mask) # TODO generalize to more dimensions result = tensor[indices] end result @@ -380,7 +380,7 @@ Returns: local result with_op_name(name, "Transpose") do if perm === nothing - r = range(constant(0), rank(n)-1) + r = range(constant(0), LinearAlgebra.rank(n)-1) perm = reverse(r, [true]) end result = Ops.transpose(n, perm) @@ -389,10 +389,10 @@ Returns: end @op function Base.permutedims(n::AbstractTensor, perm; name=nothing) - transpose(n, perm - 1; name=name) + transpose(n, perm .- 1; name=name) end -@define_unary Base.ctranspose transpose +@define_unary Base.adjoint transpose """ diff --git a/src/py.jl b/src/py.jl index 901950c6..707430bb 100644 --- a/src/py.jl +++ b/src/py.jl @@ -21,11 +21,11 @@ function py_with(f, ctx_mngr) ctx_mngr[:__exit__](nothing, nothing, nothing) end -function py_bytes(b::Vector{UInt8}) +function py_bytes(b::DenseVector{UInt8}) PyCall.PyObject(ccall(@PyCall.pysym(PyCall.PyString_FromStringAndSize), PyCall.PyPtr, (Ptr{UInt8}, Int), b, sizeof(b))) end -py_bytes(s::AbstractString) = py_bytes(Vector{UInt8}(s)) +py_bytes(s::AbstractString) = py_bytes(codeunits(s)) macro py_catch(ex) target = @match ex begin @@ -65,7 +65,7 @@ function to_protos(py_graph) protos = [] for node_idx in 1:n_nodes node_py = nodes[node_idx] - proto = Vector{UInt8}(node_py[:SerializeToString]()) + proto = codeunits(node_py[:SerializeToString]()) push!(protos, proto) end return protos @@ -76,7 +76,7 @@ function py_gradients(jl_graph_proto, x_names, y_names, grad_y_names) to_py_node(node_name) = py_graph[:get_tensor_by_name](string(node_name[1], ":", node_name[2]-1)) to_py_node(node_names::AbstractVector) = tuple(to_py_node.(node_names)...) #Need tuple as Vector will not be accepted - to_py_node(::Void) = nothing + to_py_node(::Cvoid) = nothing py_x = to_py_node(x_names) py_y = to_py_node(y_names) diff --git a/src/run.jl b/src/run.jl index 6d679367..198804d2 100644 --- a/src/run.jl +++ b/src/run.jl @@ -92,8 +92,8 @@ function run(sess::Session, inputs, input_values, outputs, targets) status = Status() output_values = fill(C_NULL, length(outputs)) input_tensors = [RawTensor(x) for x in input_values] - @tfcall(:TF_SessionRun, Void, - (Ptr{Void}, Ptr{Void}, Ptr{Void}, Ptr{Void}, Cint, Ptr{Void}, Ptr{Ptr{Void}}, Cint, Ptr{Void}, Cint, Ptr{Void}, Ptr{Void}), + @tfcall(:TF_SessionRun, Cvoid, + (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Cint, Ptr{Cvoid}, Ptr{Ptr{Cvoid}}, Cint, Ptr{Cvoid}, Cint, Ptr{Cvoid}, Ptr{Cvoid}), sess.ptr, C_NULL, inputs, @@ -107,18 +107,19 @@ function run(sess::Session, inputs, input_values, outputs, targets) C_NULL, status.ptr) check_status(status) - as_native = tensor->begin + + map(output_values) do x + tensor = RawTensor(x) if ndims(tensor) == 0 if eltype(tensor) == String - String(tensor) + convert(String, tensor) else - Number(tensor) + convert(Number, tensor) end else - Array(tensor) + convert(Array, tensor) end end - return [as_native(RawTensor(x)) for x in output_values] end function cast_type(T, val::AbstractArray{<:Number}) @@ -157,7 +158,7 @@ end function run(sess::Session, outputs::AbstractVector, input_dict) output_map = Dict{Tensor, Tuple{Symbol, Int}}() output_ports = Port[] - target_ptrs= Ptr{Void}[] + target_ptrs= Ptr{Cvoid}[] for tensor in get_tensors(outputs) if !haskey(output_map, tensor) if num_outputs(get_op(tensor)) == 0 diff --git a/src/shape_inference.jl b/src/shape_inference.jl index 7710e491..dfbb57d6 100644 --- a/src/shape_inference.jl +++ b/src/shape_inference.jl @@ -1,5 +1,6 @@ module ShapeInference +using Nullables using Compat using ..TensorFlow import TensorFlow: get_input, get_attr, TensorShape, get_shape @@ -26,10 +27,10 @@ Base.copy(shape::TensorShape) = TensorShape(copy(shape.dims), shape.rank_unknown function Base.broadcast!(s1::TensorShape, s2::TensorShape) while length(s1.dims) < length(s2.dims) - unshift!(s1.dims, Nullable(1)) + pushfirst!(s1.dims, Nullable(1)) end while length(s2.dims) < length(s1.dims) - unshift!(s2.dims, Nullable(1)) + pushfirst!(s2.dims, Nullable(1)) end s1, s2 end @@ -233,7 +234,7 @@ register_shape("Transpose") do op if isnull(maybe_reorder) [TensorShape(nothing)] else - order::Vector{Int32} = get(maybe_reorder) + 1 + order::Vector{Int32} = get(maybe_reorder) .+ 1 if input_shape.rank_unknown # We know the rank, # it must be the same as the number of elements in the perm @@ -256,11 +257,11 @@ function load_const(op) if op.op_name == "Const" local value try - value = Array(tf.load_proto(get_def(op).attr["value"])) + value = convert(Array, tf.load_proto(get_def(op).attr["value"])) catch err if isa(err, tf.EmptyTensorError) T = eltype(Tensor(op, 1)) - value = Array{T}(0) + value = Array{T}(undef, 0) else rethrow(err) end @@ -550,7 +551,7 @@ register_shape("ExpandDims") do op end function conv_sizer(widths, strides, filter_shape) - pos = ones(length(widths)) + pos = ones(Int64, length(widths)) while true while true if pos[1] + filter_shape[1] > widths[1] @@ -565,7 +566,7 @@ function conv_sizer(widths, strides, filter_shape) end pos[2] += strides[2] end - return div.(pos-1, strides)+1 + return div.(pos.-1, strides).+1 end register_shape("Conv2D") do op @@ -632,7 +633,7 @@ register_shape("MaxPool") do op push!(dims, Nullable{Int}()) end else - new_dims = 1+conv_sizer([get(input_shape.dims[2]), get(input_shape.dims[3])], [strides[2], strides[3]], [ksize[2], ksize[3]]) + new_dims = 1 .+ conv_sizer([get(input_shape.dims[2]), get(input_shape.dims[3])], [strides[2], strides[3]], [ksize[2], ksize[3]]) for i in 1:2 push!(dims, Nullable(new_dims[i])) end @@ -686,7 +687,7 @@ register_shape("Slice") do op end end end - out_shape = Vector{Int}(length(input_shape.dims)) + out_shape = Vector{Int}(undef, length(input_shape.dims)) for i in 1:length(out_shape) if size_value[i] == -1 out_shape[i] = -1 @@ -704,7 +705,7 @@ register_shape("Pad") do op if paddings.op.op_name != "Const" return [TensorShape([Nullable{Int}() for dim in 1:length(tensor_shape.dims)])] end - padding_value = Array(tf.load_proto(padding.attrs["value"])) # TODO: this might be transposed + padding_value = convert(Array, tf.load_proto(padding.attrs["value"])) # TODO: this might be transposed for dim in 1:length(tensor_shape.dims) if isnull(tensor_shape.dims[dim]) continue @@ -803,7 +804,7 @@ end register_shape("Squeeze") do op input_shape = _get_shape(get_input(op, 1)) - squeeze_dims = get_attr(op, "squeeze_dims", Vector{Int}) + 1 + squeeze_dims = get_attr(op, "squeeze_dims", Vector{Int}) .+ 1 if input_shape.rank_unknown [TensorShape(nothing)] elseif any(squeeze_dims .> length(input_shape.dims)) diff --git a/src/show.jl b/src/show.jl index 091bbdde..ea889789 100644 --- a/src/show.jl +++ b/src/show.jl @@ -1,6 +1,7 @@ using Juno using PyCall import Juno: Tree, Row, fade, interleave +import Printf @render Juno.Inline t::Tensor begin s = get_shape(t) @@ -14,12 +15,12 @@ import Juno: Tree, Row, fade, interleave end function Base.show(io::IO, s::Status) - msg = @tfcall(:TF_Message, Cstring, (Ptr{Void},), s.ptr) |> unsafe_string - print(io, @sprintf("Status: %s", msg)) + msg = @tfcall(:TF_Message, Cstring, (Ptr{Cvoid},), s.ptr) |> unsafe_string + print(io, Printf.@sprintf("Status: %s", msg)) end function Base.show(io::IO, err::TFException) - println(io, @sprintf("Tensorflow error: %s", string(err.status))) + println(io, Printf.@sprintf("Tensorflow error: %s", string(err.status))) end function Base.show(io::IO, s::Session) @@ -30,12 +31,12 @@ function Base.show(io::IO, t::RawTensor) print(io, "RawTensor: ") if ndims(t) == 0 if eltype(t) == String - show(io, String(t)) + show(io, convert(String, t)) else - show(io, Number(t)) + show(io, convert(Number, t)) end else - show(io, Array(t)) + show(io, convert(Array, t)) end end @@ -44,7 +45,7 @@ function Base.show(io::IO, n::Operation) end function Base.show(io::IO, t::Tensor{T}) where T - @assert T==eltype(t) + @assert(T==eltype(t), "eltype = $(eltype(t)), but Tensor{$(T)})") s = get_shape(t) if s.rank_unknown @@ -177,7 +178,7 @@ function get_tensorboard(logdir=nothing) if path === nothing error("The tensorboard binary was not found. Make sure `tensorboard` is in your system path.") end - _, proc = open(`$path --logdir=$logdir --port=$port`) + proc = open(`$path --logdir=$logdir --port=$port`) tensorboard[] = Tensorboard(proc, logdir, port) atexit() do close(tensorboard[]) @@ -188,9 +189,9 @@ end function open_url(url) cmd = nothing - if is_apple() + if Sys.isapple() cmd = `open $url` - elseif is_unix() + elseif Sys.isunix() cmd = `xdg-open $url` end cmd === nothing || run(cmd) diff --git a/src/summary_writer.jl b/src/summary_writer.jl index 82110d2b..b85887c6 100644 --- a/src/summary_writer.jl +++ b/src/summary_writer.jl @@ -1,10 +1,11 @@ using ProtoBuf import TensorFlow +import Distributed const tf = TensorFlow import ..TensorFlow: tensorflow, Graph, get_def_graph, @py_proc struct FileWriter - pyo::Future + pyo::Distributed.Future logdir::String end diff --git a/src/train.jl b/src/train.jl index a684a1a6..66644b22 100644 --- a/src/train.jl +++ b/src/train.jl @@ -25,6 +25,7 @@ using Compat using JLD2 using FileIO using ProtoBuf +import Printf import ..TensorFlow: Graph, Operation, get_def_graph, extend_graph, gradients, variable_scope, ConstantInitializer, node_name, get_variable, get_shape, get_collection, Session, placeholder, Tensor, Variable, cast, group, @not_implemented, AbstractQueue, tensorflow, add_to_collection, get_proto, get_def, @op @@ -178,7 +179,7 @@ function apply_gradients(optimizer::AdamOptimizer, grads_and_vars; global_step=n push!(ops, tf.scatter_update(v.var_node, grad.indices, v_new)) else m_new = β1 .* m + (1-β1).*grad - v_new = β2 .* v + (1-β2).*(grad.^2) + v_new = β2 .* v + (1-β2).*(grad.*grad) push!(ops, tf.assign_sub(var, lr/(sqrt(v_new)+ϵ) .* m_new)) push!(ops, tf.assign(m, m_new)) push!(ops, tf.assign(v, v_new)) @@ -216,7 +217,7 @@ end function FileIO.save(saver::Saver, session::Session, path; global_step=nothing) base_path = basename(path) if global_step !== nothing - path = @sprintf("%s-%d", path, global_step) + path = Printf.@sprintf("%s-%d", path, global_step) end jldopen(path, "w") do file for var_node in saver.var_list diff --git a/src/train/pipeline.jl b/src/train/pipeline.jl index 59c226d0..bf01f4b6 100644 --- a/src/train/pipeline.jl +++ b/src/train/pipeline.jl @@ -233,11 +233,11 @@ Creates tasks that run the enqueue operations in `runner` in parallel. function create_threads(runner::QueueRunner, sess) tasks = Task[] for op in runner.enqueue_ops - task = @schedule begin + task = @async begin status = tf.Status() while true try - @threadcall((:TF_SessionRun, tf.LIBTF), Void, (Ptr{Void}, Ptr{Void}, Ptr{Void}, Ptr{Void}, Cint, Ptr{Void}, Ptr{Ptr{Void}}, Cint, Ptr{Void}, Cint, Ptr{Void}, Ptr{Void}), + @threadcall((:TF_SessionRun, tf.LIBTF), Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Cint, Ptr{Cvoid}, Ptr{Ptr{Cvoid}}, Cint, Ptr{Cvoid}, Cint, Ptr{Cvoid}, Ptr{Cvoid}), sess.ptr, C_NULL, C_NULL, @@ -252,7 +252,7 @@ function create_threads(runner::QueueRunner, sess) status.ptr) tf.check_status(status) catch err - info("got $err on queue") + @info("got $err on queue") break end end diff --git a/src/variable.jl b/src/variable.jl index 4cab6a4c..b5576fd8 100644 --- a/src/variable.jl +++ b/src/variable.jl @@ -27,6 +27,9 @@ import .tf.Ops: scatter_mul, scatter_div +import Distributions +using Nullables + mutable struct Variable{T} <: tf.AbstractTensor{T} var_node::tf.Tensor{T} assign_node::tf.Tensor{T} @@ -41,7 +44,6 @@ function Variable(var_node::tf.Tensor{T}, assign_node::tf.Tensor{T}) where T v end - """ A variable maintains state in the graph across calls to `run()`. You add a variable to the graph by constructing an instance of the type `Variable`. @@ -142,7 +144,7 @@ end NormalInitializer() = NormalInitializer(.01) -Base.rand(rng::NormalInitializer, shape...) = rng.sd * randn(shape) +Base.rand(rng::NormalInitializer, shape::Integer...) = rng.sd * randn(shape) """ Gets an existing variable with these parameters (`shape`, `dtype`, `trainable`) @@ -191,7 +193,7 @@ end tf.get_tensors(v::Variable) = [v.var_node] function is_variable(name::AbstractString) - return ismatch(r"^(Variable|VariableV\d+)$", name) + return occursin(r"^(Variable|VariableV\d+)$", name) end is_variable(name::Union{tf.Operation, tf.AbstractTensor}) = is_variable(tf.get_op(name).op_name) diff --git a/src/version.jl b/src/version.jl index 18f32b96..5b44f360 100644 --- a/src/version.jl +++ b/src/version.jl @@ -1,3 +1,6 @@ +import Pkg +import InteractiveUtils + """ tf_version(kind=:backend) @@ -16,12 +19,12 @@ function tf_version(; kind=:backend) elseif kind == :python res = fetch(@py_proc py_tf[][:VERSION]) elseif kind == :julia - return Pkg.installed("TensorFlow") + return Pkg.installed()["TensorFlow"] else error("Kind '$kind' not recognized") end # Deal with version strings like "0.12.head" - res = replace(res, r"\.head$", "") + res = replace(res, r"\.head$"=>"") VersionNumber(res) end @@ -102,5 +105,5 @@ function tf_versioninfo() println("------------") println("Julia Status") println("------------") - versioninfo() + InteractiveUtils.versioninfo() end diff --git a/test/clipping.jl b/test/clipping.jl index 7466ae43..940f42a3 100644 --- a/test/clipping.jl +++ b/test/clipping.jl @@ -1,5 +1,6 @@ using TensorFlow -using Base.Test +using Test +import LinearAlgebra sess = TensorFlow.Session(TensorFlow.Graph()) a_raw = rand(10, 10) @@ -10,10 +11,10 @@ result = run(sess, clamp(a, 0.3, 0.7)) a_raw = rand(10) a = TensorFlow.constant(a_raw) result = run(sess, TensorFlow.clip_by_norm(a, 1.)) -@test normalize(a_raw) ≈ result +@test LinearAlgebra.normalize(a_raw) ≈ result b_raw = rand(10) b = TensorFlow.constant(b_raw) result = run(sess, TensorFlow.global_norm([a,b])) -gn = hypot(norm(a_raw), norm(b_raw)) +gn = hypot(LinearAlgebra.norm(a_raw), LinearAlgebra.norm(b_raw)) @test gn ≈ result diff --git a/test/comp.jl b/test/comp.jl index f7be2ca3..8f821864 100644 --- a/test/comp.jl +++ b/test/comp.jl @@ -1,5 +1,5 @@ using TensorFlow -using Base.Test +using Test sess = TensorFlow.Session(TensorFlow.Graph()) a_raw = collect(1:5) @@ -28,5 +28,5 @@ result = run(sess, TensorFlow.select(cond_tf, a, b)) @test [1; 7; 3; 4; 10] == result -@test run(sess, find(constant([true,true, false,true]))) == [1 2 4]' -@test run(sess, find(constant([true true false true; false true false true]))) == [1 1; 1 2; 1 4; 2 2; 2 4] +@test run(sess, findall(constant([true,true, false,true]))) == [1 2 4]' +@test run(sess, findall(constant([true true false true; false true false true]))) == [1 1; 1 2; 1 4; 2 2; 2 4] diff --git a/test/control.jl b/test/control.jl index fc9fa0c4..531b0994 100644 --- a/test/control.jl +++ b/test/control.jl @@ -1,6 +1,6 @@ -using Base.Test +using Test using TensorFlow - +import LinearAlgebra @testset "identity and make_tuple" begin sess = Session(Graph()) @@ -17,7 +17,7 @@ end y = constant(5) f1 = ()->17x f2 = ()->y+23 - result = run(sess, cond(xdiag_mat))) @test prod(diag_raw) ≈ result end @@ -69,8 +73,8 @@ end @test a_raw - b_raw ≈ result result = run(sess, a.*b) @test a_raw.*b_raw ≈ result - result = run(sess, a × b) - @test a_raw × b_raw ≈ result + result = run(sess, LinearAlgebra.cross(a, b)) + @test LinearAlgebra.cross(a_raw, b_raw) ≈ result result = run(sess, TensorFlow.add_n([a,b])) @test a_raw .+ b_raw ≈ result result = run(sess, 5 * a) @@ -89,20 +93,20 @@ end a_raw = rand(10) a = TensorFlow.constant(a_raw) - result = run(sess, indmin(a, 1)) - @test indmin(a_raw) == result - result = run(sess, indmax(a, 1)) - @test indmax(a_raw) == result + result = run(sess, argmin(a, 1)) + @test argmin(a_raw) == result + result = run(sess, argmax(a, 1)) + @test argmax(a_raw) == result #check it find the first instance of lowest/highers number as the result for indmin/indmax x=constant([1 2 3; 0 2 3; 0 0 3; 0 0 0]') - @test [2, 3, 4] == run(sess, indmin(x, 2)) - @test [1, 1, 1] == run(sess, indmax(x, 2)) + @test [2, 3, 4] == run(sess, argmin(x, 2)) + @test [1, 1, 1] == run(sess, argmax(x, 2)) end @testset "logic" begin - a_raw = Vector{Bool}(bitrand(10)) - b_raw = Vector{Bool}(bitrand(10)) + a_raw = Vector{Bool}(Random.bitrand(10)) + b_raw = Vector{Bool}(Random.bitrand(10)) a = TensorFlow.constant(a_raw) b = TensorFlow.constant(b_raw) for (op, func) in [(&, logical_and), (|, logical_or)] @@ -117,18 +121,18 @@ end @testset "reduce" begin a_raw = rand(10) a = TensorFlow.constant(a_raw) - for (jl_func, red_func) in [(sum, reduce_sum), (prod, reduce_prod), (minimum, reduce_min), (maximum, reduce_max), (mean, reduce_mean)] + for (jl_func, red_func) in [(sum, reduce_sum), (prod, reduce_prod), (minimum, reduce_min), (maximum, reduce_max), (Statistics.mean, reduce_mean)] result = run(sess, red_func(a)) @test jl_func(a_raw) ≈ result result = run(sess, red_func(a, axis=0)) - @test jl_func(a_raw, 1)[1] ≈ result + @test jl_func(a_raw; dims=1)[1] ≈ result @test run(sess, jl_func(a)) ≈ jl_func(a_raw) end a_raw = rand(10) a = TensorFlow.constant(a_raw) inds = vcat(ones(Int32,5), fill(Int32(2), 5)) - for (jl_func, seg_func) in [(sum, segment_sum), (prod, segment_prod), (minimum, segment_min), (maximum, segment_max), (mean, segment_mean)] + for (jl_func, seg_func) in [(sum, segment_sum), (prod, segment_prod), (minimum, segment_min), (maximum, segment_max), (Statistics.mean, segment_mean)] result = run(sess, seg_func(a, inds)) @test jl_func(a_raw[1:5]) ≈ result[1] @test jl_func(a_raw[6:10]) ≈ result[2] @@ -136,7 +140,7 @@ end a_raw = Vector{Bool}(trues(10)) a = TensorFlow.constant(a_raw) - b_raw = Vector{Bool}(bitrand(10)) + b_raw = Vector{Bool}(Random.bitrand(10)) b = TensorFlow.constant(b_raw) for (jl_func, red_func) in [(any, reduce_any), (all, reduce_all)] result = run(sess, red_func(a)) @@ -146,7 +150,7 @@ end end # Unsorted segment sum - a = TensorFlow.constant(eye(5)) + a = TensorFlow.constant(LinearAlgebra.Diagonal(ones(5,5))) idxs = TensorFlow.constant(map(Int64, [1,1,2,3,1])) n = TensorFlow.constant(Int32(3)) d = unsorted_segment_sum(a, idxs, n) @@ -157,7 +161,7 @@ end end @testset "linear algebra" begin - M_raw = tril(rand(Float32, 10, 10)) + M_raw = LinearAlgebra.tril(rand(Float32, 10, 10)) x_raw = ones(Float32, 10, 2) x = TensorFlow.constant(x_raw) M = TensorFlow.constant(M_raw) @@ -171,29 +175,29 @@ end M_raw = rand(Float32, 10, 10) M_raw += M_raw' result = run(sess, TensorFlow.self_adjoint_eig(constant(M_raw))) - @test eigvals(M_raw) ≈ result[1] - evs = eigvecs(M_raw) + @test LinearAlgebra.eigvals(M_raw) ≈ result[1] + evs = LinearAlgebra.eigvecs(M_raw) for vec_ind in 1:10 - @test abs(dot(evs[:, vec_ind], result[2][:, vec_ind])) ≈ Float32(1.) + @test abs(LinearAlgebra.dot(evs[:, vec_ind], result[2][:, vec_ind])) ≈ Float32(1.) end M_raw = rand(Float32, 10, 10) M_raw *= M_raw' result = run(sess, TensorFlow.cholesky(constant(M_raw))) - @test cholfact(M_raw)[:L] ≈ result + @test LinearAlgebra.cholesky(M_raw).L ≈ result - for thin in [true, false] + for full in [true, false] M_raw = rand(Float32, 10, 10) - u, s, v = run(sess, svd(constant(M_raw), thin=thin)) - uj, sj, vj = svd(M_raw, thin=thin) + u, s, v = run(sess, LinearAlgebra.svd(constant(M_raw), full=full)) + uj, sj, vj = LinearAlgebra.svd(M_raw, full=full) # Can't directly check values between julia svd and tensorflow svd as SVD not unique # Check the shape of results match @test size(s) == size(sj) @test size(u) == size(uj) @test size(v) == size(uj) # Check the reconstruction is close - err = sum(abs2, M_raw-u*diagm(s)*v') - err_j = sum(abs2, M_raw-uj*diagm(sj)*vj') + err = sum(abs2, M_raw-u*LinearAlgebra.diagm(0=>s)*v') + err_j = sum(abs2, M_raw-uj*LinearAlgebra.diagm(0=>sj)*vj') @assert err_j ≤ 1e-10 @test err ≤ 1000err_j # Julia has really accurate SVD, apparently, so give it a margin diff --git a/test/meta.jl b/test/meta.jl index d1ac8e0c..ff4a8b05 100644 --- a/test/meta.jl +++ b/test/meta.jl @@ -1,4 +1,4 @@ -using Base.Test +using Test using TensorFlow @testset "Registering Ops" begin diff --git a/test/nn.jl b/test/nn.jl index 142a0168..6ef29e52 100644 --- a/test/nn.jl +++ b/test/nn.jl @@ -1,6 +1,8 @@ using TensorFlow -using Base.Test +using Test using StatsFuns +using Random +import LinearAlgebra @testset "conv2d_transpose" begin let @@ -17,11 +19,12 @@ end @testset "Cross Entropy Loss" begin - srand(1) + Random.seed!(1) let sess = Session(Graph()) targets = constant(collect(1:10)) - targets_hot = constant(Float64.(eye(10))) + #targets_hot = constant(Float64.(LinearAlgebra.eye(10))) + targets_hot = constant(Matrix{Float64}(LinearAlgebra.I, 10, 10)) logits_unscaled = constant(rand(10,10)) @@ -186,8 +189,8 @@ for (rnn_fun, post_proc_outputs) in ((nn.rnn, last),) # the output from the first sequence is repeated 3 times since the length is 1 # the second output from the second and fourth sequence is repeated twice # the third sequence has some new output from each of the 3 time steps - @test all(outs_jl[1] .== outs_jl[2], 2) == [true false false false]' - @test all(outs_jl[2] .== outs_jl[3], 2) == [true true false true]' + @test all(outs_jl[1] .== outs_jl[2], dims=2) == [true false false false]' + @test all(outs_jl[2] .== outs_jl[3], dims=2) == [true true false true]' # since xdata is the same for all sequences the hidden state will be the same # if the sequences have equal length. Sequence number 2 and 4 are both of length 2. diff --git a/test/ops.jl b/test/ops.jl index 2a2dfd59..672b23bb 100644 --- a/test/ops.jl +++ b/test/ops.jl @@ -1,5 +1,5 @@ using TensorFlow -using Base.Test +using Test @testset "Importing" begin diff --git a/test/proto.jl b/test/proto.jl index c43f2940..8053441b 100644 --- a/test/proto.jl +++ b/test/proto.jl @@ -4,5 +4,5 @@ using TensorFlow: load_proto let t = get_def(constant("test")).attr["value"].tensor - @test Array(load_proto(t)) == Vector{UInt8}("test") + @test convert(Array, load_proto(t)) == Vector{UInt8}("test") end diff --git a/test/run.jl b/test/run.jl index a5cf85ff..3fdd4360 100644 --- a/test/run.jl +++ b/test/run.jl @@ -1,4 +1,4 @@ -using Base.Test +using Test using TensorFlow @testset "Placeholder Size Matching" begin @@ -37,7 +37,7 @@ using TensorFlow end @testset begin - srand(1) + Random.seed!(1) data = rand(Int64.(1:10), 3,4) sess = Session(Graph()) diff --git a/test/runtests.jl b/test/runtests.jl index e680af6c..e590de71 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,7 +1,8 @@ using TensorFlow -using Base.Test +using Test +import Random -srand(0) # Make tests deterministic +Random.seed!(0) # Make tests deterministic include(joinpath(dirname(@__FILE__), "..", "examples", "logistic.jl")) @@ -32,14 +33,6 @@ tests = [ "transformations.jl", ] -# @test_nowarn was added in Julia 0.6. -# We make it a no-op on earlier versions. -if !isdefined(Base.Test, Symbol("@test_nowarn")) - macro test_nowarn(ex) - esc(ex) - end -end - tf_versioninfo() # Dump out all the info at start of the test, for easy debugging from logs. (also check `tf_versioninfo()` itself works) diff --git a/test/sequences.jl b/test/sequences.jl index b389ad75..7ea8f962 100644 --- a/test/sequences.jl +++ b/test/sequences.jl @@ -1,5 +1,5 @@ using TensorFlow -using Base.Test +using Test sess = Session(Graph()) @@ -12,7 +12,7 @@ sess = Session(Graph()) x = random_normal(shape, dtype=dtype) @test get_shape(x) == TensorShape(shape) result = run(sess, x) - @test size(result) == (shape...) + @test size(result) == (shape...,) @test eltype(result) == dtype end end diff --git a/test/shape_inference.jl b/test/shape_inference.jl index 26db952f..af6fc208 100644 --- a/test/shape_inference.jl +++ b/test/shape_inference.jl @@ -1,5 +1,6 @@ using TensorFlow -using Base.Test +using Test +using Nullables k = placeholder(Float32; shape=[10, 20, -1]) m = placeholder(Float32; shape=[10, 20, 30]) @@ -48,7 +49,7 @@ end @testset "$f" for f in [max,min] @testset "$xs against scalar" for xs in [:k, :m, :n, :i] - x = eval(xs) + x = Core.eval(Main, xs) @test get_shape(f(x,i)) == get_shape(x) @test get_shape(f(x,x)) == get_shape(x) end @@ -56,9 +57,9 @@ end end @testset "Find (i.e Where)" begin - @test get_shape(find(placeholder(Bool; shape=[10, 20, 30]))) == TensorShape([-1,3]) - @test get_shape(find(placeholder(Bool; shape=[10, 20, -1]))) == TensorShape([-1,3]) - @test get_shape(find(placeholder(Bool))) == TensorShape(nothing) + @test get_shape(findall(placeholder(Bool; shape=[10, 20, 30]))) == TensorShape([-1,3]) + @test get_shape(findall(placeholder(Bool; shape=[10, 20, -1]))) == TensorShape([-1,3]) + @test get_shape(findall(placeholder(Bool))) == TensorShape(nothing) end @testset "Stack/Unstack" begin @@ -92,7 +93,7 @@ end @testset "ArgMinMax" begin - @testset "$f" for f in (indmin, indmax, Ops.arg_min, Ops.arg_max) + @testset "$f" for f in (argmin, argmax, Ops.arg_min, Ops.arg_max) @test get_shape(f(k, 1)) == TensorShape([20, -1]) @test get_shape(f(k, 2)) == TensorShape([10, -1]) @test get_shape(f(k, 3)) == TensorShape([10, 20]) @@ -154,7 +155,7 @@ end @testset "Squeeze" begin let x = placeholder(Float64, shape=[5, 1, 4, 1, 3]) - y = squeeze(x, [2, 4]) + y = dropdims(x, dims=[2, 4]) @test get_shape(y) == TensorShape([5, 4, 3]) end end @@ -172,16 +173,16 @@ end e = placeholder(Float32; shape=[10, 20, 30]) e2 = placeholder(Float32; shape=[10, 20, 31]) - @testset "$r" for (a1, a2, r) in [ - (m, e, [10, 20, 30]) - (m, k, [10, 20, -1]) - (m, e2, nothing) - (m, n, nothing) - (m, i, nothing) + @testset "$name" for (a1, a2, r, name) in [ + (m, e, [10, 20, 30], "t1") + (m, k, [10, 20, -1], "t2") + (m, e2, nothing, "t3") + (m, n, nothing, "t4") + (m, i, nothing, "t5") ] # test commutativeness - @test get_shape(select(c, a1, a2)) == TensorShape(r) - @test get_shape(select(c, a2, a1)) == TensorShape(r) + @test get_shape(TensorFlow.select(c, a1, a2)) == TensorShape(r) + @test get_shape(TensorFlow.select(c, a2, a1)) == TensorShape(r) end end diff --git a/test/show.jl b/test/show.jl index 41f88a85..3c9f82c1 100644 --- a/test/show.jl +++ b/test/show.jl @@ -1,5 +1,5 @@ using TensorFlow -using Base.Test +using Test @testset "Tensorboard" begin TensorFlow.get_tensorboard() diff --git a/test/summary.jl b/test/summary.jl index 6810536f..67290f76 100644 --- a/test/summary.jl +++ b/test/summary.jl @@ -1,6 +1,6 @@ using TensorFlow const tf = TensorFlow -using Base.Test +using Test graph = Graph() sess = Session(graph) diff --git a/test/train.jl b/test/train.jl index d5897105..7a52bf50 100644 --- a/test/train.jl +++ b/test/train.jl @@ -1,5 +1,5 @@ using TensorFlow -using Base.Test +using Test @testset "save and resore" begin try diff --git a/test/training.jl b/test/training.jl index 6f8cc597..668adb61 100644 --- a/test/training.jl +++ b/test/training.jl @@ -1,4 +1,4 @@ -using Base.Test +using Test using TensorFlow @testset "Training with 'gather' nodes" begin diff --git a/test/transformations.jl b/test/transformations.jl index 2d196ac7..7a3460eb 100644 --- a/test/transformations.jl +++ b/test/transformations.jl @@ -1,6 +1,7 @@ using TensorFlow -using Base.Test - +using Test +using Random +import LinearAlgebra sess = TensorFlow.Session(TensorFlow.Graph()) @@ -8,7 +9,7 @@ one_tens = ones(Tensor, (5,5)) @test [1, 2] == run(sess, cast(constant([1.8, 2.2]), Int)) @test ones(25) == run(sess, reshape(one_tens, 25)) -@test 2 == run(sess, rank(one_tens)) +@test 2 == run(sess, LinearAlgebra.rank(one_tens)) @test ones(10,5) == run(sess, tile(one_tens, [2; 1])) @test hcat(ones(Float32, 5,5), zeros(Float32, 5)) == run(sess, pad(one_tens, [0 0; 0 1])) @test Float32[1.; 0.; 0.; 0.; 0.] == run(sess, one_hot(1, 5)) @@ -44,13 +45,12 @@ end end -@testset "Squeeze" begin - # Test `squeeze()` works when given explicit dimensions, fails on incorrect explicit dimensions, +@testset "Dropdims" begin + # Test `dropdims()` works when given explicit dimensions, fails on incorrect explicit dimensions, # and works when given no explicit dimension sq_ones = ones(Tensor, (10, 1, 5, 1)) - @test size(run(sess, squeeze(sq_ones))) == (10,5) - @test size(run(sess, squeeze(sq_ones,[2,4]))) == (10,5) - @test size(run(sess, squeeze(sq_ones,[2]))) == (10,5,1) + @test size(run(sess, dropdims(sq_ones, dims=[2,4]))) == (10,5) + @test size(run(sess, dropdims(sq_ones, dims=[2]))) == (10,5,1) #@test_throws TensorFlow.TFException run(sess, squeeze(sq_ones,[1])) end @@ -111,7 +111,7 @@ end @testset "Slice" begin # to do make sure we slice the right indices - @test ones(Float32, 5).' == run(sess, TensorFlow.slice(one_tens, [1, 1], [1, -1])) + @test transpose(ones(Float32, 5)) == run(sess, TensorFlow.slice(one_tens, [1, 1], [1, -1])) @test y_jl[2:3] == run(sess, y[2:3]) @test y_jl[2:end] == run(sess, y[Int32(2):end]) @@ -149,7 +149,7 @@ end # Tests after this point must provide their own sessions and graphs @testset "Concatenation Syntax" begin - srand(37) + Random.seed!(37) sess4 = Session(Graph()) a_jl = rand(10,5); a = constant(a_jl);