diff --git a/Project.toml b/Project.toml index fec56db8..8f2f62d7 100644 --- a/Project.toml +++ b/Project.toml @@ -6,6 +6,7 @@ version = "3.15.1" [deps] Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" +CommonSolve = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2" DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e" Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" @@ -23,7 +24,9 @@ Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46" SimpleDiffEq = "05bca326-078c-5bf0-a5bf-ce7c7982d7fd" SimpleNonlinearSolve = "727e6d20-b764-4bd8-a329-72de5adea6c7" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" ZygoteRules = "700de1a5-db45-46bc-99cf-38207098b444" [weakdeps] @@ -47,27 +50,35 @@ AMDGPU = "2" Adapt = "4" CUDA = "5, 6" ChainRulesCore = "1" +CommonSolve = "0.2" DiffEqBase = "7" +Distributed = "1" DocStringExtensions = "0.9" ForwardDiff = "1" GPUArraysCore = "0.2" JLArrays = "0.3" KernelAbstractions = "0.9.38" +LinearAlgebra = "1" LinearSolve = "3" Metal = "1" MuladdMacro = "0.2.4" OpenCL = "0.10" Parameters = "0.12, 0.13" +Random = "1" RecursiveArrayTools = "4" SciMLBase = "3.1" Setfield = "1" SimpleDiffEq = "1.11" SimpleNonlinearSolve = "2" StaticArrays = "1.9.14" +StaticArraysCore = "1.4" TOML = "1" +Test = "1" +UnPack = "1" ZygoteRules = "0.2.7" julia = "1.10" oneAPI = "2" +pocl_jll = "7" [extras] AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" diff --git a/src/DiffEqGPU.jl b/src/DiffEqGPU.jl index 00ee068b..05194c71 100644 --- a/src/DiffEqGPU.jl +++ b/src/DiffEqGPU.jl @@ -3,38 +3,58 @@ $(DocStringExtensions.README) """ module DiffEqGPU -using DocStringExtensions -using KernelAbstractions +using DocStringExtensions: DocStringExtensions +using KernelAbstractions: KernelAbstractions, @Const, @index, @kernel, CPU import KernelAbstractions: get_backend, allocate -using SciMLBase, DiffEqBase, LinearAlgebra, Distributed -using ForwardDiff +using SciMLBase: SciMLBase, CallbackSet, CheckInit, ContinuousCallback, + DiscreteCallback, EnsembleDistributed, EnsembleProblem, + EnsembleSerial, EnsembleSolution, EnsembleThreads, ODEFunction, + ODEProblem, ReturnCode, SDEFunction, SDEProblem, + VectorContinuousCallback, remake, terminate! +using DiffEqBase: DiffEqBase, BrownFullBasicInit +using LinearAlgebra: LinearAlgebra, I, LowerTriangular, NoPivot, RowMaximum, + SingularException, UpperTriangular, det +using Distributed: Distributed, nprocs, pmap +using ForwardDiff: ForwardDiff import ChainRulesCore import ChainRulesCore: NoTangent -using RecursiveArrayTools +using RecursiveArrayTools: RecursiveArrayTools, VectorOfArray import ZygoteRules import Base.Threads -using LinearSolve -using SimpleNonlinearSolve +using Base: setindex +using CommonSolve: solve +using LinearSolve: LinearSolve +using SimpleNonlinearSolve: SimpleNonlinearSolve import SimpleNonlinearSolve: SimpleTrustRegion #For gpu_tsit5 -using Adapt, SimpleDiffEq, StaticArrays -using Parameters, MuladdMacro -using Random -using Setfield -using ForwardDiff -import StaticArrays: StaticVecOrMat, @_inline_meta -# import LinearAlgebra: \ -import StaticArrays: LU, StaticLUMatrix, arithmetic_closure +using Adapt: Adapt, adapt +using SimpleDiffEq: SimpleDiffEq, GPUSimpleATsit5, GPUSimpleAVern7, GPUSimpleAVern9, + GPUSimpleTsit5, GPUSimpleVern7, GPUSimpleVern9, SimpleEM +using StaticArrays: StaticArrays +using StaticArraysCore: MArray, MMatrix, SArray, SMatrix, SVector, Size, + StaticMatrix, StaticVector, similar_type +using Parameters: Parameters +using MuladdMacro: MuladdMacro, @muladd +using Random: Random +using Setfield: Setfield, @set, @set! +using UnPack: @unpack +# StaticArraysCore-owned type alias (re-exported by StaticArrays); used in dispatch. +import StaticArrays: StaticVecOrMat +# Non-public StaticArrays internals used by the vendored GPU LU/linsolve kernels. +import StaticArrays: @_inline_meta, LU, StaticLUMatrix import SciMLBase: ImmutableODEProblem abstract type EnsembleArrayAlgorithm <: SciMLBase.EnsembleAlgorithm end abstract type EnsembleKernelAlgorithm <: SciMLBase.EnsembleAlgorithm end ##Solvers for EnsembleGPUKernel -abstract type GPUODEAlgorithm <: DiffEqBase.AbstractODEAlgorithm end -abstract type GPUSDEAlgorithm <: DiffEqBase.AbstractSDEAlgorithm end +abstract type GPUODEAlgorithm <: SciMLBase.AbstractODEAlgorithm end +abstract type GPUSDEAlgorithm <: SciMLBase.AbstractSDEAlgorithm end abstract type GPUODEImplicitAlgorithm{AD} <: GPUODEAlgorithm end +_unwrap_val(B) = B +_unwrap_val(::Val{B}) where {B} = B + include("ensemblegpuarray/callbacks.jl") include("ensemblegpuarray/kernels.jl") include("ensemblegpuarray/problem_generation.jl") diff --git a/src/ensemblegpukernel/alg_utils.jl b/src/ensemblegpukernel/alg_utils.jl index b9701aaa..6ceb1c41 100644 --- a/src/ensemblegpukernel/alg_utils.jl +++ b/src/ensemblegpukernel/alg_utils.jl @@ -15,7 +15,7 @@ alg_order(alg::GPUEM) = 1 alg_order(alg::GPUSIEA) = 2 function finite_diff_jac(f, jac_prototype, x) - dx = sqrt(eps(DiffEqBase.RecursiveArrayTools.recursive_bottom_eltype(x))) + dx = sqrt(eps(RecursiveArrayTools.recursive_bottom_eltype(x))) jac = MMatrix{size(x, 1), size(x, 1), eltype(x)}(1I) for i in eachindex(x) x_dx = convert(MArray, x) diff --git a/src/ensemblegpukernel/gpukernel_algorithms.jl b/src/ensemblegpukernel/gpukernel_algorithms.jl index ce6e4d3f..5d5dbfda 100644 --- a/src/ensemblegpukernel/gpukernel_algorithms.jl +++ b/src/ensemblegpukernel/gpukernel_algorithms.jl @@ -66,7 +66,7 @@ struct GPUKvaerno5{AD} <: GPUODEImplicitAlgorithm{AD} end for Alg in [:GPURosenbrock23, :GPURodas4, :GPURodas5P, :GPUKvaerno3, :GPUKvaerno5] @eval begin function $Alg(; autodiff = Val{true}()) - return $Alg{SciMLBase._unwrap_val(autodiff)}() + return $Alg{_unwrap_val(autodiff)}() end end end diff --git a/src/ensemblegpukernel/integrators/integrator_utils.jl b/src/ensemblegpukernel/integrators/integrator_utils.jl index f8302b44..1216bca5 100644 --- a/src/ensemblegpukernel/integrators/integrator_utils.jl +++ b/src/ensemblegpukernel/integrators/integrator_utils.jl @@ -11,7 +11,7 @@ function build_adaptive_controller_cache(alg::A, ::Type{T}) where {A, T} end @inline function savevalues!( - integrator::DiffEqBase.AbstractODEIntegrator{ + integrator::SciMLBase.AbstractODEIntegrator{ AlgType, IIP, S, @@ -50,7 +50,7 @@ end end @inline function DiffEqBase.terminate!( - integrator::DiffEqBase.AbstractODEIntegrator{ + integrator::SciMLBase.AbstractODEIntegrator{ AlgType, IIP, S, T, @@ -67,7 +67,7 @@ end end @inline function apply_discrete_callback!( - integrator::DiffEqBase.AbstractODEIntegrator{ + integrator::SciMLBase.AbstractODEIntegrator{ AlgType, IIP, S, T, @@ -91,7 +91,7 @@ end end @inline function apply_discrete_callback!( - integrator::DiffEqBase.AbstractODEIntegrator{ + integrator::SciMLBase.AbstractODEIntegrator{ AlgType, IIP, S, T, @@ -111,7 +111,7 @@ end end @inline function apply_discrete_callback!( - integrator::DiffEqBase.AbstractODEIntegrator{ + integrator::SciMLBase.AbstractODEIntegrator{ AlgType, IIP, S, T, @@ -137,7 +137,7 @@ end end @inline function apply_discrete_callback!( - integrator::DiffEqBase.AbstractODEIntegrator{ + integrator::SciMLBase.AbstractODEIntegrator{ AlgType, IIP, S, T, @@ -156,7 +156,7 @@ end end @inline function interpolate( - integrator::DiffEqBase.AbstractODEIntegrator{ + integrator::SciMLBase.AbstractODEIntegrator{ AlgType, IIP, S, @@ -176,7 +176,7 @@ end end @inline function _change_t_via_interpolation!( - integrator::DiffEqBase.AbstractODEIntegrator{ + integrator::SciMLBase.AbstractODEIntegrator{ AlgType, IIP, S, @@ -205,7 +205,7 @@ end end end @inline function DiffEqBase.change_t_via_interpolation!( - integrator::DiffEqBase.AbstractODEIntegrator{ + integrator::SciMLBase.AbstractODEIntegrator{ AlgType, IIP, S, @@ -227,7 +227,7 @@ end end @inline function apply_callback!( - integrator::DiffEqBase.AbstractODEIntegrator{ + integrator::SciMLBase.AbstractODEIntegrator{ AlgType, IIP, S, T, }, @@ -272,7 +272,7 @@ end end @inline function handle_callbacks!( - integrator::DiffEqBase.AbstractODEIntegrator{ + integrator::SciMLBase.AbstractODEIntegrator{ AlgType, IIP, S, T, }, @@ -384,7 +384,7 @@ end end @inline function DiffEqBase.find_callback_time( - integrator::DiffEqBase.AbstractODEIntegrator{ + integrator::SciMLBase.AbstractODEIntegrator{ AlgType, IIP, S, @@ -442,7 +442,7 @@ end end @inline function SciMLBase.get_tmp_cache( - integrator::DiffEqBase.AbstractODEIntegrator{ + integrator::SciMLBase.AbstractODEIntegrator{ AlgType, IIP, S, T, @@ -458,7 +458,7 @@ end end @inline function DiffEqBase.get_condition( - integrator::DiffEqBase.AbstractODEIntegrator{ + integrator::SciMLBase.AbstractODEIntegrator{ AlgType, IIP, S, T, diff --git a/src/ensemblegpukernel/integrators/nonstiff/interpolants.jl b/src/ensemblegpukernel/integrators/nonstiff/interpolants.jl index a2ce9d40..a72811cb 100644 --- a/src/ensemblegpukernel/integrators/nonstiff/interpolants.jl +++ b/src/ensemblegpukernel/integrators/nonstiff/interpolants.jl @@ -1,7 +1,7 @@ # Default: Hermite Interpolation @inline @muladd function _ode_interpolant( Θ, dt, y₀, - integ::DiffEqBase.AbstractODEIntegrator{ + integ::SciMLBase.AbstractODEIntegrator{ AlgType, IIP, S, T, } diff --git a/src/ensemblegpukernel/integrators/nonstiff/types.jl b/src/ensemblegpukernel/integrators/nonstiff/types.jl index 7a1c4737..7df0fce3 100644 --- a/src/ensemblegpukernel/integrators/nonstiff/types.jl +++ b/src/ensemblegpukernel/integrators/nonstiff/types.jl @@ -1,7 +1,7 @@ ## Fixed TimeStep Integrator mutable struct GPUTsit5Integrator{IIP, S, T, ST, P, F, TS, CB, AlgType} <: - DiffEqBase.AbstractODEIntegrator{AlgType, IIP, S, T} + SciMLBase.AbstractODEIntegrator{AlgType, IIP, S, T} alg::AlgType f::F # eom uprev::S # previous state @@ -52,7 +52,7 @@ DiffEqBase.isinplace(::GPUT5I{IIP}) where {IIP} = IIP ## Adaptive TimeStep Integrator mutable struct GPUATsit5Integrator{IIP, S, T, ST, P, F, N, TOL, Q, TS, CB, AlgType} <: - DiffEqBase.AbstractODEIntegrator{AlgType, IIP, S, T} + SciMLBase.AbstractODEIntegrator{AlgType, IIP, S, T} alg::AlgType f::F # eom uprev::S # previous state @@ -108,7 +108,7 @@ end ## Vern7 mutable struct GPUV7Integrator{IIP, S, T, ST, P, F, TS, CB, TabType, AlgType} <: - DiffEqBase.AbstractODEIntegrator{AlgType, IIP, S, T} + SciMLBase.AbstractODEIntegrator{AlgType, IIP, S, T} alg::AlgType f::F # eom uprev::S # previous state @@ -152,7 +152,7 @@ const GPUV7I = GPUV7Integrator end mutable struct GPUAV7Integrator{IIP, S, T, ST, P, F, N, TOL, Q, TS, CB, TabType, AlgType} <: - DiffEqBase.AbstractODEIntegrator{AlgType, IIP, S, T} + SciMLBase.AbstractODEIntegrator{AlgType, IIP, S, T} alg::AlgType f::F # eom uprev::S # previous state @@ -205,7 +205,7 @@ end ## Vern9 mutable struct GPUV9Integrator{IIP, S, T, ST, P, F, TS, CB, TabType, AlgType} <: - DiffEqBase.AbstractODEIntegrator{AlgType, IIP, S, T} + SciMLBase.AbstractODEIntegrator{AlgType, IIP, S, T} alg::AlgType f::F # eom uprev::S # previous state @@ -249,7 +249,7 @@ const GPUV9I = GPUV9Integrator end mutable struct GPUAV9Integrator{IIP, S, T, ST, P, F, N, TOL, Q, TS, CB, TabType, AlgType} <: - DiffEqBase.AbstractODEIntegrator{AlgType, IIP, S, T} + SciMLBase.AbstractODEIntegrator{AlgType, IIP, S, T} alg::AlgType f::F # eom uprev::S # previous state diff --git a/src/ensemblegpukernel/integrators/stiff/types.jl b/src/ensemblegpukernel/integrators/stiff/types.jl index 96bf1772..b63e601f 100644 --- a/src/ensemblegpukernel/integrators/stiff/types.jl +++ b/src/ensemblegpukernel/integrators/stiff/types.jl @@ -1,5 +1,5 @@ mutable struct GPURosenbrock23Integrator{IIP, S, T, ST, P, F, TS, CB, AlgType} <: - DiffEqBase.AbstractODEIntegrator{AlgType, IIP, S, T} + SciMLBase.AbstractODEIntegrator{AlgType, IIP, S, T} alg::AlgType f::F # eom uprev::S # previous state @@ -77,7 +77,7 @@ mutable struct GPUARosenbrock23Integrator{ CB, AlgType, } <: - DiffEqBase.AbstractODEIntegrator{AlgType, IIP, S, T} + SciMLBase.AbstractODEIntegrator{AlgType, IIP, S, T} alg::AlgType f::F # eom uprev::S # previous state @@ -181,7 +181,7 @@ end ########################## # Fixed Step mutable struct GPURodas4Integrator{IIP, S, T, ST, P, F, TS, CB, TabType, AlgType} <: - DiffEqBase.AbstractODEIntegrator{AlgType, IIP, S, T} + SciMLBase.AbstractODEIntegrator{AlgType, IIP, S, T} alg::AlgType f::F # eom uprev::S # previous state @@ -268,7 +268,7 @@ mutable struct GPUARodas4Integrator{ TabType, AlgType, } <: - DiffEqBase.AbstractODEIntegrator{AlgType, IIP, S, T} + SciMLBase.AbstractODEIntegrator{AlgType, IIP, S, T} alg::AlgType f::F # eom uprev::S # previous state @@ -370,7 +370,7 @@ end ########################## # Fixed Step mutable struct GPURodas5PIntegrator{IIP, S, T, ST, P, F, TS, CB, TabType, AlgType} <: - DiffEqBase.AbstractODEIntegrator{AlgType, IIP, S, T} + SciMLBase.AbstractODEIntegrator{AlgType, IIP, S, T} alg::AlgType f::F # eom uprev::S # previous state @@ -449,7 +449,7 @@ mutable struct GPUARodas5PIntegrator{ IIP, S, T, ST, P, F, N, TOL, Q, TS, CB, TabType, AlgType, } <: - DiffEqBase.AbstractODEIntegrator{AlgType, IIP, S, T} + SciMLBase.AbstractODEIntegrator{AlgType, IIP, S, T} alg::AlgType f::F # eom uprev::S # previous state @@ -553,7 +553,7 @@ end ########################## # Fixed Step mutable struct GPUKvaerno3Integrator{IIP, S, T, ST, P, F, TS, CB, TabType, AlgType} <: - DiffEqBase.AbstractODEIntegrator{AlgType, IIP, S, T} + SciMLBase.AbstractODEIntegrator{AlgType, IIP, S, T} alg::AlgType f::F # eom uprev::S # previous state @@ -632,7 +632,7 @@ mutable struct GPUAKvaerno3Integrator{ IIP, S, T, ST, P, F, N, TOL, Q, TS, CB, TabType, AlgType, } <: - DiffEqBase.AbstractODEIntegrator{AlgType, IIP, S, T} + SciMLBase.AbstractODEIntegrator{AlgType, IIP, S, T} alg::AlgType f::F # eom uprev::S # previous state @@ -735,7 +735,7 @@ end ########################## # Fixed Step mutable struct GPUKvaerno5Integrator{IIP, S, T, ST, P, F, TS, CB, TabType, AlgType} <: - DiffEqBase.AbstractODEIntegrator{AlgType, IIP, S, T} + SciMLBase.AbstractODEIntegrator{AlgType, IIP, S, T} alg::AlgType f::F # eom uprev::S # previous state @@ -814,7 +814,7 @@ mutable struct GPUAKvaerno5Integrator{ IIP, S, T, ST, P, F, N, TOL, Q, TS, CB, TabType, AlgType, } <: - DiffEqBase.AbstractODEIntegrator{AlgType, IIP, S, T} + SciMLBase.AbstractODEIntegrator{AlgType, IIP, S, T} alg::AlgType f::F # eom uprev::S # previous state diff --git a/src/ensemblegpukernel/nlsolve/type.jl b/src/ensemblegpukernel/nlsolve/type.jl index cf861755..33aca977 100644 --- a/src/ensemblegpukernel/nlsolve/type.jl +++ b/src/ensemblegpukernel/nlsolve/type.jl @@ -53,7 +53,7 @@ end @inline function build_J_W(alg, f, γ, dt) J(u, p, t) = - if DiffEqBase.has_jac(f) + if SciMLBase.has_jac(f) f.jac(u, p, t) elseif alg_autodiff(alg) ForwardDiff.jacobian(u -> f(u, p, t), u) @@ -66,7 +66,7 @@ end @inline function build_tgrad(alg, f) function tgrad(u, p, t) - return if DiffEqBase.has_tgrad(f) + return if SciMLBase.has_tgrad(f) f.tgrad(u, p, t) elseif alg_autodiff(alg) ForwardDiff.derivative(t -> f(u, p, t), t) diff --git a/test/qa/Project.toml b/test/qa/Project.toml index 2939dc85..9282b032 100644 --- a/test/qa/Project.toml +++ b/test/qa/Project.toml @@ -1,7 +1,17 @@ [deps] +Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" DiffEqGPU = "071ae1c0-96b5-11e9-1965-c90190d839ea" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" +SciMLTesting = "09d9d899-5365-40a9-917a-5f67fddea283" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[compat] +Aqua = "0.8" +ForwardDiff = "1" +JET = "0.9, 0.10" +SciMLBase = "3.30" +SciMLTesting = "1.7" +StaticArrays = "1.9.14" diff --git a/test/qa/qa.jl b/test/qa/qa.jl new file mode 100644 index 00000000..f64083c1 --- /dev/null +++ b/test/qa/qa.jl @@ -0,0 +1,51 @@ +using SciMLTesting, DiffEqGPU, Test +using JET + +run_qa( + DiffEqGPU; + explicit_imports = true, + ei_kwargs = (; + # StaticVecOrMat is re-exported by StaticArrays but owned by StaticArraysCore. + # It is a non-public type alias used only in method-signature dispatch for the + # vendored GPU linear-solve kernels; importing it from its true owner would + # still leave it non-public, so the via-owners exception is the natural place. + all_explicit_imports_via_owners = (; ignore = (:StaticVecOrMat,)), + # Non-public names accessed qualified from upstream packages. These are genuine + # internal/extension APIs; they will drop out of this list as those packages + # declare `public` (verified flagged against the registered releases on Julia + # 1.12, where these checks run). + all_qualified_accesses_are_public = (; + ignore = ( + # SciMLBase callback/rootfind/ensemble internals (not yet `public`) + :AbstractContinuousCallback, :AbstractDiscreteCallback, + :DEFAULT_REDUCTION, :FINALIZE_DEFAULT, :INITIALIZE_DEFAULT, + :LeftRootFind, :NoRootFind, :RootfindOpt, :build_linear_solution, + :default_rng_func, :generate_sim_seeds, :solve_batch, + :specialization, :tighten_container_eltype, + # ForwardDiff differentiation API (documented but not `public`) + :Chunk, :Dual, :Partials, :construct_seeds, :derivative, :jacobian, + :npartials, :partials, :value, + # LinearSolve cache/algorithm extension interface (not `public`) + :LinearCache, :SciMLLinearSolveAlgorithm, :init_cacheval, + :needs_concrete_A, + # SimpleDiffEq Tsit5 tableau-cache internals (not `public`) + :_build_atsit5_caches, :_build_tsit5_caches, :bθs, + # LinearAlgebra Hermitian/Symmetric union (stdlib, not `public`) + :HermOrSym, + # Core compiler inference used to size a Channel/Vector eltype; no + # public cross-version replacement (Base.infer_return_type is 1.11+, + # and the LTS floor is Julia 1.10). + :Compiler, :return_type, + ), + ), + # Non-public names imported explicitly from upstream packages. The + # StaticArrays/StaticArraysCore internals back the vendored GPU LU/linsolve + # kernels; `setindex` is the immutable Base helper used by the GPU LU pivot. + all_explicit_imports_are_public = (; + ignore = ( + :var"@_inline_meta", :LU, :StaticLUMatrix, :StaticVecOrMat, + :StaticMatrix, :StaticVector, :similar_type, :setindex, + ), + ), + ), +) diff --git a/test/runtests.jl b/test/runtests.jl index 0ef9fe74..2987b8a6 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -24,9 +24,13 @@ if GROUP == "QA" Pkg.activate(joinpath(@__DIR__, "qa")) Pkg.develop(Pkg.PackageSpec(path = dirname(@__DIR__))) Pkg.instantiate() + @time @safetestset "Quality Assurance" begin + include("qa/qa.jl") + end @time @safetestset "JET static analysis" begin include("jet_tests.jl") end + exit() end @time @safetestset "GPU Kernelized Stiff ODE Mass Matrix" begin