diff --git a/.github/workflows/CompatHelper.yml b/.github/workflows/CompatHelper.yml index c9a9d01a..55fcb2ab 100644 --- a/.github/workflows/CompatHelper.yml +++ b/.github/workflows/CompatHelper.yml @@ -13,4 +13,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} COMPATHELPER_PRIV: ${{ secrets.COMPATHELPER_PRIV }} # optional - run: julia -e 'using CompatHelper; CompatHelper.main(; subdirs = ["","lib/MadNLPGPU","lib/MadNLPGraph","lib/MadNLPHSL","lib/MadNLPKrylov","lib/MadNLPMumps","lib/MadNLPPardiso","lib/MadNLPTests"])' + run: julia -e 'using CompatHelper; CompatHelper.main(; subdirs = ["","lib/MadNLPGPU","lib/MadNLPHSL","lib/MadNLPKrylov","lib/MadNLPMumps","lib/MadNLPPardiso","lib/MadNLPTests"])' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dec9feb4..5b7574d4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,7 +42,7 @@ jobs: - run: julia --color=yes --project=.ci .ci/ci.jl full - uses: julia-actions/julia-processcoverage@v1 with: - directories: src,lib/MadNLPHSL/src,lib/MadNLPPardiso/src,lib/MadNLPMumps/src,lib/MadNLPKrylov/src,lib/MadNLPGraph + directories: src,lib/MadNLPHSL/src,lib/MadNLPPardiso/src,lib/MadNLPMumps/src,lib/MadNLPKrylov/src - uses: codecov/codecov-action@v1 with: file: lcov.info diff --git a/lib/MadNLPGraph/.gitignore b/lib/MadNLPGraph/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/MadNLPGraph/LICENSE b/lib/MadNLPGraph/LICENSE deleted file mode 100644 index a612ad98..00000000 --- a/lib/MadNLPGraph/LICENSE +++ /dev/null @@ -1,373 +0,0 @@ -Mozilla Public License Version 2.0 -================================== - -1. Definitions --------------- - -1.1. "Contributor" - means each individual or legal entity that creates, contributes to - the creation of, or owns Covered Software. - -1.2. "Contributor Version" - means the combination of the Contributions of others (if any) used - by a Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - means Source Code Form to which the initial Contributor has attached - the notice in Exhibit A, the Executable Form of such Source Code - Form, and Modifications of such Source Code Form, in each case - including portions thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - (a) that the initial Contributor has attached the notice described - in Exhibit B to the Covered Software; or - - (b) that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the - terms of a Secondary License. - -1.6. "Executable Form" - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - means a work that combines Covered Software with other material, in - a separate file or files, that is not Covered Software. - -1.8. "License" - means this document. - -1.9. "Licensable" - means having the right to grant, to the maximum extent possible, - whether at the time of the initial grant or subsequently, any and - all of the rights conveyed by this License. - -1.10. "Modifications" - means any of the following: - - (a) any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered - Software; or - - (b) any new file in Source Code Form that contains any Covered - Software. - -1.11. "Patent Claims" of a Contributor - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the - License, by the making, using, selling, offering for sale, having - made, import, or transfer of either its Contributions or its - Contributor Version. - -1.12. "Secondary License" - means either the GNU General Public License, Version 2.0, the GNU - Lesser General Public License, Version 2.1, the GNU Affero General - Public License, Version 3.0, or any later versions of those - licenses. - -1.13. "Source Code Form" - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that - controls, is controlled by, or is under common control with You. For - purposes of this definition, "control" means (a) the power, direct - or indirect, to cause the direction or management of such entity, - whether by contract or otherwise, or (b) ownership of more than - fifty percent (50%) of the outstanding shares or beneficial - ownership of such entity. - -2. License Grants and Conditions --------------------------------- - -2.1. Grants - -Each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: - -(a) under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - -(b) under Patent Claims of such Contributor to make, use, sell, offer - for sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -(a) for any code that a Contributor has removed from Covered Software; - or - -(b) for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - -(c) under Patent Claims infringed by Covered Software in the absence of - its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -3. Responsibilities -------------------- - -3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -(a) such Covered Software must also be made available in Source Code - Form, as described in Section 3.1, and You must inform recipients of - the Executable Form how they can obtain a copy of such Source Code - Form by reasonable means in a timely manner, at a charge no more - than the cost of distribution to the recipient; and - -(b) You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter - the recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - -You may choose to offer, and to charge a fee for, warranty, support, -indemnity or liability obligations to one or more recipients of Covered -Software. However, You may do so only on Your own behalf, and not on -behalf of any Contributor. You must make it absolutely clear that any -such warranty, support, indemnity, or liability obligation is offered by -You alone, and You hereby agree to indemnify every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -4. Inability to Comply Due to Statute or Regulation ---------------------------------------------------- - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -5. Termination --------------- - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - -************************************************************************ -* * -* 6. Disclaimer of Warranty * -* ------------------------- * -* * -* Covered Software is provided under this License on an "as is" * -* basis, without warranty of any kind, either expressed, implied, or * -* statutory, including, without limitation, warranties that the * -* Covered Software is free of defects, merchantable, fit for a * -* particular purpose or non-infringing. The entire risk as to the * -* quality and performance of the Covered Software is with You. * -* Should any Covered Software prove defective in any respect, You * -* (not any Contributor) assume the cost of any necessary servicing, * -* repair, or correction. This disclaimer of warranty constitutes an * -* essential part of this License. No use of any Covered Software is * -* authorized under this License except under this disclaimer. * -* * -************************************************************************ - -************************************************************************ -* * -* 7. Limitation of Liability * -* -------------------------- * -* * -* Under no circumstances and under no legal theory, whether tort * -* (including negligence), contract, or otherwise, shall any * -* Contributor, or anyone who distributes Covered Software as * -* permitted above, be liable to You for any direct, indirect, * -* special, incidental, or consequential damages of any character * -* including, without limitation, damages for lost profits, loss of * -* goodwill, work stoppage, computer failure or malfunction, or any * -* and all other commercial damages or losses, even if such party * -* shall have been informed of the possibility of such damages. This * -* limitation of liability shall not apply to liability for death or * -* personal injury resulting from such party's negligence to the * -* extent applicable law prohibits such limitation. Some * -* jurisdictions do not allow the exclusion or limitation of * -* incidental or consequential damages, so this exclusion and * -* limitation may not apply to You. * -* * -************************************************************************ - -8. Litigation -------------- - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -9. Miscellaneous ----------------- - -This License represents the complete agreement concerning the subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -10. Versions of the License ---------------------------- - -10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice -------------------------------------------- - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice ---------------------------------------------------------- - - This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the Mozilla Public License, v. 2.0. diff --git a/lib/MadNLPGraph/Project.toml b/lib/MadNLPGraph/Project.toml deleted file mode 100644 index d471b371..00000000 --- a/lib/MadNLPGraph/Project.toml +++ /dev/null @@ -1,27 +0,0 @@ -name = "MadNLPGraph" -uuid = "2f590f30-ea46-438a-a78d-e5bfe3109704" -authors = ["Sungho Shin "] -version = "0.2.0" - -[deps] -MadNLP = "2621e9c9-9eb4-46b1-8089-e8c72242dfb6" -Plasmo = "d3f7391f-f14a-50cc-bbe4-76a32d1bad3c" -JuMP = "4076af6c-e467-56ae-b986-b466b2749572" -LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d" -Metis = "2679e427-3c69-5b7f-982b-ece356f1e94b" - -[compat] -MadNLP = "~0.4" -JuMP = "~0.22" -Plasmo = "~0.4" -LightGraphs = "~1.3" -Metis = "1" -julia = "1.6" -MadNLPTests = "~0.2" - -[extras] -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -MadNLPTests = "b52a2a03-04ab-4a5f-9698-6a2deff93217" - -[targets] -test = ["Test","MadNLPTests"] diff --git a/lib/MadNLPGraph/src/MadNLPGraph.jl b/lib/MadNLPGraph/src/MadNLPGraph.jl deleted file mode 100644 index 8898b965..00000000 --- a/lib/MadNLPGraph/src/MadNLPGraph.jl +++ /dev/null @@ -1,29 +0,0 @@ -module MadNLPGraph - -import MadNLP: - optimize!, Optimizer, InteriorPointSolver, - MOIU, MOI, get_nnz_hess, get_nnz_jac, set_x!, set_g!, INITIAL, is_jac_hess_constant, - jacobian_structure, hessian_lagrangian_structure, eval_objective, - eval_objective_gradient, eval_function, eval_constraint, eval_hessian_lagrangian, eval_constraint_jacobian, - @kwdef, Logger, @debug, @warn, @error, @sprintf, - AbstractOptions, AbstractLinearSolver, EmptyLinearSolver, set_options!, - SparseMatrixCSC, SubVector, - SymbolicException,FactorizationException,SolveException,InertiaException, - introduce, factorize!, solve!, improve!, is_inertia, inertia, - default_linear_solver, default_dense_solver, get_csc_view, get_cscsy_view, nnz, mul!, - set_blas_num_threads, blas_num_threads, @blas_safe_threads, - MOIModel, NLPModelsCounters, MadNLPExecutionStats, AbstractNLPModel, NLPModelMeta, - get_lcon, get_ucon, jac_structure!, hess_structure!, obj, grad!, cons!, jac_coord!, hess_coord! -import Metis: partition -import LightGraphs: Graph, Edge, add_edge!, edges, src, dst, neighbors, nv -import Plasmo: OptiGraph, OptiNode, OptiEdge, all_nodes, all_edges, all_variables, num_all_nodes, link_constraints, getnode, num_variables, num_constraints -import JuMP: _create_nlp_block_data, set_optimizer, GenericAffExpr - -include("plasmo_interface.jl") -include("graphtools.jl") -include("schur.jl") -include("schwarz.jl") - -export MadNLPSchur, MadNLPSchwarz - -end # module diff --git a/lib/MadNLPGraph/src/graphtools.jl b/lib/MadNLPGraph/src/graphtools.jl deleted file mode 100644 index ba5b25f6..00000000 --- a/lib/MadNLPGraph/src/graphtools.jl +++ /dev/null @@ -1,140 +0,0 @@ -# MadNLP.jl. -# Created by Sungho Shin (sungho.shin@wisc.edu) - -mutable struct MonolevelPartition - g::Graph - nparts::Int - part::Vector{Int} -end - -mutable struct MonolevelStruc - V::Vector{Int} - new_nbr::Vector{Int} -end - -mutable struct BilevelPartition - g_lower::Graph - nparts_lower::Int - part_lower::Vector{Int} - V_lower::Vector{Vector{Int}} - - g_upper::Graph - nparts_upper::Int - part_upper::Vector{Int} -end - -mutable struct BilevelStruc - V_lower::Vector{Int} - V_upper::Vector{Int} - new_nbr_upper::Vector{Int} -end - -mutable struct TwoStagePartition - nparts::Int - part::Vector{Int} -end - -get_current_V(mls::MonolevelStruc) = mls.V -get_current_size(mls::MonolevelStruc) = length(mls.V) -get_full_size(mlp::MonolevelPartition) = nv(mlp.g) - -function MonolevelPartition(csc::SparseMatrixCSC,part,nparts;max_size=0.) - g = Graph(csc) - isempty(part) && (part=partition(g,nparts,alg=:KWAY)) - return MonolevelPartition(g,nparts,part) -end - -function MonolevelStruc(mlp::MonolevelPartition,k;max_size=0.) - V = findall(mlp.part.==k) - new_nbr = expand!(V,mlp.g,max_size) - return MonolevelStruc(V,new_nbr) -end - -function expand!(mls::MonolevelStruc,mlp::MonolevelPartition,max_size) - mls.new_nbr = expand!(mls.V,mlp.g,max_size,new_nbr=mls.new_nbr) - return -end - -get_current_V(bls::BilevelStruc) = bls.V_lower -get_current_size(bls::BilevelStruc) = length(bls.V_upper) -get_full_size(blp::BilevelPartition) = blp.nparts_lower - -function BilevelPartition(csc,part_lower,nparts_lower,part_upper,nparts_upper;max_size=0.) - g_lower = Graph(csc) - isempty(part_lower) && (part_lower= partition(g_lower,nparts_lower,alg=:KWAY)) - V_lower = Vector{Vector{Int}}(undef,nparts_lower) - @blas_safe_threads for k=1:nparts_lower - V_lower[k] = findall(part_lower.==k) - end - - g_upper = Graph(nparts_lower) - for e in edges(g_lower) - add_edge!(g_upper,part_lower[src(e)],part_lower[dst(e)]) - end - isempty(part_upper) && (part_upper = partition(g_upper,nparts_upper,alg=:KWAY)) - - return BilevelPartition(g_lower,nparts_lower,part_lower,V_lower,g_upper,nparts_upper,part_upper) -end - -function BilevelStruc(blp::BilevelPartition,k;max_size=0.) - V_upper = findall(blp.part_upper.==k) - new_nbr_upper = expand!(V_upper,blp.g_upper,max_size) - V_lower = vcat(blp.V_lower[V_upper]...) - - return BilevelStruc(V_lower,V_upper,new_nbr_upper) -end - -function TwoStagePartition(csc::SparseMatrixCSC,part,nparts) - if isempty(part) || findfirst(x->x==0.,part) == nothing - g = Graph(csc) - isempty(part) && (part = partition(g,nparts,alg=:KWAY)) - mark_boundary!(g,part) - end - return TwoStagePartition(nparts,part) -end - -Graph(csc::SparseMatrixCSC) = Graph(getelistcsc(csc.colptr,csc.rowval)) -getelistcsc(colptr,rowval) = [Edge(i,Int(j)) for i=1:length(colptr)-1 for j in @view rowval[colptr[i]:colptr[i+1]-1]] - -function expand!(bls::BilevelStruc,blp::BilevelPartition,max_size) - orig_size = length(bls.V_upper) - bls.new_nbr_upper = expand!(bls.V_upper,blp.g_upper,max_size,new_nbr=bls.new_nbr_upper) - bls.V_lower = vcat(blp.V_lower[bls.V_upper]...) - return -end - -function expand!(V_om,g::Graph,max_size; - new_nbr=[]) - if isempty(new_nbr) - new_nbr = Int[] - for v in V_om - append!(new_nbr,neighbors(g,v)) - end - unique!(new_nbr) - setdiff!(new_nbr,V_om) - end - - old_nbr = V_om - - while (length(V_om) + length(new_nbr) < max_size) && length(V_om) < nv(g) && !isempty(new_nbr) - append!(V_om,new_nbr) - old_old_nbr = old_nbr - old_nbr=new_nbr - new_nbr = Int[] - for v in old_nbr - append!(new_nbr,neighbors(g,v)) - end - unique!(new_nbr) - setdiff!(new_nbr,old_old_nbr) - setdiff!(new_nbr,old_nbr) - end - - return new_nbr -end - -function mark_boundary!(g,part) - for e in edges(g) - (part[src(e)]!=part[dst(e)] && part[src(e)]!= 0 && part[dst(e)] != 0) && - (part[src(e)] = 0; part[dst(e)] = 0) - end -end diff --git a/lib/MadNLPGraph/src/plasmo_interface.jl b/lib/MadNLPGraph/src/plasmo_interface.jl deleted file mode 100644 index 20e35b5b..00000000 --- a/lib/MadNLPGraph/src/plasmo_interface.jl +++ /dev/null @@ -1,408 +0,0 @@ -const dummy_function = ()->nothing - -num_linkconstraints(modeledge::OptiEdge) = length(modeledge.linkconstraints) -function _caching_optimizer(modelnode::OptiNode) - if isa(modelnode.model.moi_backend,MOIU.CachingOptimizer) - return modelnode.model.moi_backend - else - return modelnode.model.moi_backend.optimizer - end -end -moi_optimizer(modelnode::OptiNode) = _caching_optimizer(modelnode).optimizer.model -_caching_optimizer(model::Any) = model.moi_backend -function set_g_link!(linkedge::OptiEdge,l,gl,gu) - cnt = 1 - for (ind,linkcon) in linkedge.linkconstraints - l[cnt] = 0. # need to implement dual start later - if linkcon.set isa MOI.EqualTo - gl[cnt] = linkcon.set.value - gu[cnt] = linkcon.set.value - elseif linkcon.set isa MOI.GreaterThan - gl[cnt] = linkcon.set.lower - gu[cnt] = Inf - elseif linkcon.set isa MOI.LessThan - gl[cnt] = -Inf - gu[cnt] = linkcon.set.upper - else - gl[cnt] = linkcon.set.lower - gu[cnt] = linkcon.set.upper - end - cnt += 1 - end -end - -function hessian_lagrangian_structure(graph::OptiGraph,I,J,ninds,nnzs_hess_inds,modelnodes) - @blas_safe_threads for k=1:length(modelnodes) - isempty(nnzs_hess_inds[k]) && continue - offset = ninds[k][1]-1 - II = view(I,nnzs_hess_inds[k]) - JJ = view(J,nnzs_hess_inds[k]) - hessian_lagrangian_structure( - moi_optimizer(modelnodes[k]),II,JJ) - II.+= offset - JJ.+= offset - end -end - -function jacobian_structure(linkedge::OptiEdge,I,J,ninds,x_index_map,g_index_map) - offset=1 - for linkcon in link_constraints(linkedge) - offset += jacobian_structure(linkcon,I,J,ninds,x_index_map,g_index_map,offset) - end -end -function jacobian_structure(linkcon,I,J,ninds,x_index_map,g_index_map,offset) - cnt = 0 - for var in get_vars(linkcon) - I[offset+cnt] = g_index_map[linkcon] - J[offset+cnt] = x_index_map[var] - cnt += 1 - end - return cnt -end - -function jacobian_structure( - graph::OptiGraph,I,J,ninds,minds,pinds,nnzs_jac_inds,nnzs_link_jac_inds, - x_index_map,g_index_map,modelnodes,linkedges) - - @blas_safe_threads for k=1:length(modelnodes) - isempty(nnzs_jac_inds[k]) && continue - offset_i = minds[k][1]-1 - offset_j = ninds[k][1]-1 - II = view(I,nnzs_jac_inds[k]) - JJ = view(J,nnzs_jac_inds[k]) - jacobian_structure( - moi_optimizer(modelnodes[k]),II,JJ) - II.+= offset_i - JJ.+= offset_j - end - - @blas_safe_threads for q=1:length(linkedges) - isempty(nnzs_link_jac_inds[q]) && continue - II = view(I,nnzs_link_jac_inds[q]) - JJ = view(J,nnzs_link_jac_inds[q]) - jacobian_structure( - linkedges[q],II,JJ,ninds,x_index_map,g_index_map) - end -end -function eval_objective(graph::OptiGraph,x,ninds,x_index_map,modelnodes) - obj = Threads.Atomic{Float64}(0.) - @blas_safe_threads for k=1:length(modelnodes) - Threads.atomic_add!(obj,eval_objective( - moi_optimizer(modelnodes[k]),view(x,ninds[k]))) - end - return obj.value + eval_function(graph.objective_function,x,ninds,x_index_map) -end -function eval_objective_gradient(graph::OptiGraph,f,x,ninds,modelnodes) - @blas_safe_threads for k=1:length(modelnodes) - eval_objective_gradient(moi_optimizer(modelnodes[k]),view(f,ninds[k]),view(x,ninds[k])) - end -end - -function eval_function(aff::GenericAffExpr,x,ninds,x_index_map) - function_value = aff.constant - for (var,coef) in aff.terms - function_value += coef*x[x_index_map[var]] - end - return function_value -end -function eval_constraint(linkedge::OptiEdge,c,x,ninds,x_index_map) - cnt = 1 - for linkcon in link_constraints(linkedge) - c[cnt] = eval_function(get_func(linkcon),x,ninds,x_index_map) - cnt += 1 - end -end -get_func(linkcon) = linkcon.func -function eval_constraint(graph::OptiGraph,c,x,ninds,minds,pinds,x_index_map,modelnodes,linkedges) - @blas_safe_threads for k=1:length(modelnodes) - eval_constraint(moi_optimizer(modelnodes[k]),view(c,minds[k]),view(x,ninds[k])) - end - @blas_safe_threads for q=1:length(linkedges) - eval_constraint(linkedges[q],view(c,pinds[q]),x,ninds,x_index_map) - end -end - -function eval_hessian_lagrangian(graph::OptiGraph,hess,x,sig,l, - ninds,minds,nnzs_hess_inds,modelnodes) - @blas_safe_threads for k=1:length(modelnodes) - isempty(nnzs_hess_inds) && continue - eval_hessian_lagrangian(moi_optimizer(modelnodes[k]), - view(hess,nnzs_hess_inds[k]),view(x,ninds[k]),sig, - view(l,minds[k])) - end -end - -function eval_constraint_jacobian(linkedge::OptiEdge,jac,x) - offset=0 - for linkcon in link_constraints(linkedge) - offset+=eval_constraint_jacobian(linkcon,jac,offset) - end -end -function eval_constraint_jacobian(linkcon,jac,offset) - cnt = 0 - for coef in get_coeffs(linkcon) - cnt += 1 - jac[offset+cnt] = coef - end - return cnt -end -get_vars(linkcon) = keys(linkcon.func.terms) -get_coeffs(linkcon) = values(linkcon.func.terms) - -function eval_constraint_jacobian(graph::OptiGraph,jac,x, - ninds,minds,nnzs_jac_inds,nnzs_link_jac_inds,modelnodes,linkedges) - @blas_safe_threads for k=1:length(modelnodes) - eval_constraint_jacobian( - moi_optimizer(modelnodes[k]),view(jac,nnzs_jac_inds[k]),view(x,ninds[k])) - end - @blas_safe_threads for q=1:length(linkedges) - eval_constraint_jacobian(linkedges[q],view(jac,nnzs_link_jac_inds[q]),x) - end -end -get_nnz_link_jac(linkedge::OptiEdge) = sum( - length(linkcon.func.terms) for (ind,linkcon) in linkedge.linkconstraints) - - -struct GraphModel{T} <: AbstractNLPModel{T,Vector{T}} - ninds::Vector{UnitRange{Int}} - minds::Vector{UnitRange{Int}} - pinds::Vector{UnitRange{Int}} - nnzs_jac_inds::Vector{UnitRange{Int}} - nnzs_hess_inds::Vector{UnitRange{Int}} - nnzs_link_jac_inds::Vector{UnitRange{Int}} - - x_index_map::Dict # - g_index_map::Dict # - - modelnodes::Vector{OptiNode} - linkedges::Vector{OptiEdge} - - meta::NLPModelMeta{T, Vector{T}} - graph::OptiGraph - counters::NLPModelsCounters - ext::Dict{Symbol,Any} -end - -obj(nlp::GraphModel, x::AbstractVector) = eval_objective(nlp.graph,x,nlp.ninds,nlp.x_index_map,nlp.modelnodes) -grad!(nlp::GraphModel, x::AbstractVector, f::AbstractVector) =eval_objective_gradient(nlp.graph,f,x,nlp.ninds,nlp.modelnodes) -cons!(nlp::GraphModel, x::AbstractVector, c::AbstractVector) = eval_constraint( - nlp.graph,c,x,nlp.ninds,nlp.minds,nlp.pinds,nlp.x_index_map,nlp.modelnodes,nlp.linkedges) -function hess_coord!(nlp::GraphModel,x::AbstractVector,l::AbstractVector,hess::AbstractVector;obj_weight=1.) - eval_hessian_lagrangian( - nlp.graph,hess,x,obj_weight,l,nlp.ninds,nlp.minds, - nlp.nnzs_hess_inds,nlp.modelnodes, - ) -end -function jac_coord!(nlp::GraphModel,x::AbstractVector,jac::AbstractVector) - eval_constraint_jacobian( - nlp.graph,jac,x,nlp.ninds,nlp.minds,nlp.nnzs_jac_inds,nlp.nnzs_link_jac_inds, - nlp.modelnodes,nlp.linkedges, - ) -end -function hess_structure!(nlp::GraphModel, I::AbstractVector{T}, J::AbstractVector{T}) where T - hessian_lagrangian_structure( - nlp.graph,I,J,nlp.ninds,nlp.nnzs_hess_inds,nlp.modelnodes, - ) -end -function jac_structure!(nlp::GraphModel, I::AbstractVector{T}, J::AbstractVector{T}) where T - jacobian_structure( - nlp.graph,I,J,nlp.ninds,nlp.minds,nlp.pinds,nlp.nnzs_jac_inds,nlp.nnzs_link_jac_inds, - nlp.x_index_map,nlp.g_index_map,nlp.modelnodes,nlp.linkedges, - ) -end - -function GraphModel(graph::OptiGraph) - modelnodes = all_nodes(graph) - linkedges = all_edges(graph) - - for modelnode in modelnodes - num_variables(modelnode) == 0 && error("Empty node exist! Delete the empty nodes.") - end - - @blas_safe_threads for k=1:length(modelnodes) - set_optimizer(modelnodes[k],Optimizer) - if modelnodes[k].model.nlp_data !== nothing - MOI.set(modelnodes[k].model, MOI.NLPBlock(), - _create_nlp_block_data(modelnodes[k].model)) - empty!(modelnodes[k].model.nlp_data.nlconstr_duals) - end - MOIU.attach_optimizer(modelnodes[k].model) - MOI.initialize(moi_optimizer(modelnodes[k]).nlp_data.evaluator,[:Grad,:Hess,:Jac]) - end - - K = length(modelnodes) - ns= [num_variables(modelnode) for modelnode in modelnodes] - n = sum(ns) - ns_cumsum = cumsum(ns) - ms= [num_constraints(modelnode) for modelnode in modelnodes] - ms_cumsum = cumsum(ms) - m = sum(ms) - - nnzs_hess = [get_nnz_hess(moi_optimizer(modelnode)) for modelnode in modelnodes] - nnzs_hess_cumsum = cumsum(nnzs_hess) - nnz_hess = sum(nnzs_hess) - - nnzs_jac = [get_nnz_jac(moi_optimizer(modelnode)) for modelnode in modelnodes] - nnzs_jac_cumsum = cumsum(nnzs_jac) - nnz_jac = sum(nnzs_jac) - - nnzs_link_jac = [get_nnz_link_jac(linkedge) for linkedge in linkedges] - nnzs_link_jac_cumsum = cumsum(nnzs_link_jac) - nnz_link_jac = isempty(nnzs_link_jac) ? 0 : sum(nnzs_link_jac) - - ninds = [(i==1 ? 0 : ns_cumsum[i-1])+1:ns_cumsum[i] for i=1:K] - minds = [(i==1 ? 0 : ms_cumsum[i-1])+1:ms_cumsum[i] for i=1:K] - nnzs_hess_inds = [(i==1 ? 0 : nnzs_hess_cumsum[i-1])+1:nnzs_hess_cumsum[i] for i=1:K] - nnzs_jac_inds = [(i==1 ? 0 : nnzs_jac_cumsum[i-1])+1:nnzs_jac_cumsum[i] for i=1:K] - - Q = length(linkedges) - ps= [num_linkconstraints(modeledge) for modeledge in linkedges] - ps_cumsum = cumsum(ps) - p = sum(ps) - pinds = [(i==1 ? m : m+ps_cumsum[i-1])+1:m+ps_cumsum[i] for i=1:Q] - nnzs_link_jac_inds = - [(i==1 ? nnz_jac : nnz_jac+nnzs_link_jac_cumsum[i-1])+1: nnz_jac + nnzs_link_jac_cumsum[i] for i=1:Q] - - x =Vector{Float64}(undef,n) - xl=Vector{Float64}(undef,n) - xu=Vector{Float64}(undef,n) - - l =Vector{Float64}(undef,m+p) - gl=Vector{Float64}(undef,m+p) - gu=Vector{Float64}(undef,m+p) - - @blas_safe_threads for k=1:K - set_x!(moi_optimizer(modelnodes[k]),view(x,ninds[k]),view(xl,ninds[k]),view(xu,ninds[k])) - set_g!(moi_optimizer(modelnodes[k]),view(l,minds[k]),view(gl,minds[k]),view(gu,minds[k])) - end - - @blas_safe_threads for q=1:Q - set_g_link!(linkedges[q],view(l,pinds[q]),view(gl,pinds[q]),view(gu,pinds[q])) - end - - modelmap=Dict(modelnodes[k].model=> k for k=1:K) - x_index_map = Dict( - # var=>ninds[modelmap[var.model]][backend(var.model).optimizer.model_to_optimizer_map[var.index].value] - var=>ninds[modelmap[var.model]][_caching_optimizer(getnode(var)).model_to_optimizer_map[var.index].value] - for modelnode in modelnodes for var in all_variables(modelnode)) - cnt = 0 - g_index_map = Dict(con=> m + (cnt+=1) for linkedge in linkedges for (ind,con) in linkedge.linkconstraints) - - jac_constant = true - hess_constant = true - for node in modelnodes - j,h = is_jac_hess_constant(moi_optimizer(node)) - jac_constant = jac_constant & j - hess_constant = hess_constant & h - end - - ext = Dict{Symbol,Any}(:n=>n,:m=>m,:p=>p,:ninds=>ninds,:minds=>minds,:pinds=>pinds, - :linkedges=>linkedges,:jac_constant=>jac_constant,:hess_constant=>hess_constant) - - return GraphModel( - ninds,minds,pinds,nnzs_jac_inds,nnzs_hess_inds,nnzs_link_jac_inds, - x_index_map,g_index_map,modelnodes,linkedges, - NLPModelMeta( - n, - ncon = m+p, - x0 = x, - lvar = xl, - uvar = xu, - y0 = l, - lcon = gl, - ucon = gu, - nnzj = nnz_jac + nnz_link_jac, - nnzh = nnz_hess, - minimize = true # graph.objective_sense == MOI.MIN_SENSE - ), - graph,NLPModelsCounters(), - ext - ) - -end - -function get_part(graph::OptiGraph,nlp::GraphModel) - n = nlp.ext[:n] - m = nlp.ext[:m] - p = nlp.ext[:p] - - ninds = nlp.ninds - minds = nlp.minds - pinds = nlp.pinds - - ind_ineq = findall(get_lcon(nlp).!=get_ucon(nlp)) - l = length(ind_ineq) - - part = Vector{Int}(undef,n+m+l+p) - - for k=1:length(ninds) - part[ninds[k]].=k - end - for k=1:length(minds) - part[minds[k].+n.+l].=k - end - cnt = 0 - - for linkedge in nlp.ext[:linkedges] - for (ind,con) in linkedge.linkconstraints - cnt+=1 - attached_node_idx = graph.node_idx_map[con.attached_node] - part[n+l+m+cnt] = attached_node_idx != nothing ? attached_node_idx : error("All the link constraints need to be attached to a node") - end - end - - cnt = 0 - for q in ind_ineq - cnt+=1 - part[n+cnt] = part[n+l+q] - end - - - return part -end - -function optimize!(graph::OptiGraph; option_dict = Dict{Symbol,Any}(), kwargs...) - graph.objective_function.constant = 0. - nlp = GraphModel(graph) - - K = num_all_nodes(graph) - if (haskey(kwargs,:schwarz_custom_partition) && kwargs[:schwarz_custom_partition]) || - (haskey(option_dict,:schwarz_custom_partition) && option_dict[:schwarz_custom_partition]) - - part= get_part(graph,nlp) - option_dict[:schwarz_part] = part - option_dict[:schwarz_num_parts] = num_all_nodes(graph) - - elseif (haskey(kwargs,:schur_custom_partition) && kwargs[:schur_custom_partition]) || - (haskey(option_dict,:schur_custom_partition) && option_dict[:schur_custom_partition]) - - part= get_part(graph,nlp) - part[part.>K].=0 - option_dict[:schur_part] = part - option_dict[:schur_num_parts] = K - end - - option_dict[:jacobian_constant] = nlp.ext[:jac_constant] - option_dict[:hessian_constant] = nlp.ext[:hess_constant] - ips = InteriorPointSolver(nlp;option_dict=option_dict,kwargs...) - result = optimize!(ips) - - graph.optimizer = ips - graph.objective_function.constant = graph.optimizer.obj_val - - @blas_safe_threads for k=1:K - moi_optimizer(nlp.modelnodes[k]).result = MadNLPExecutionStats( - ips.status,view(result.solution,nlp.ninds[k]), - ips.obj_val, - view(result.constraints,nlp.minds[k]), - ips.inf_du, ips.inf_pr, - view(result.multipliers,nlp.minds[k]), - view(result.multipliers_L,nlp.ninds[k]), - view(result.multipliers_U,nlp.ninds[k]), - ips.cnt.k, ips.nlp.counters,ips.cnt.total_time) - # TODO: quick hack to specify to JuMP that the - # model is not dirty (so we do not run in `OptimizeNotCalled` - # exception). - nlp.modelnodes[k].model.is_model_dirty = false - end -end diff --git a/lib/MadNLPGraph/src/schur.jl b/lib/MadNLPGraph/src/schur.jl deleted file mode 100644 index 9876f382..00000000 --- a/lib/MadNLPGraph/src/schur.jl +++ /dev/null @@ -1,192 +0,0 @@ -module MadNLPSchur - -import ..MadNLPGraph: - @kwdef, Logger, @debug, @warn, @error, - AbstractOptions, AbstractLinearSolver, EmptyLinearSolver, set_options!, SparseMatrixCSC, SubVector, - SymbolicException,FactorizationException,SolveException,InertiaException, - introduce, factorize!, solve!, improve!, is_inertia, inertia, - default_linear_solver, default_dense_solver, get_csc_view, get_cscsy_view, nnz, mul!, - TwoStagePartition, set_blas_num_threads, blas_num_threads, @blas_safe_threads - -const INPUT_MATRIX_TYPE = :csc - - -@kwdef mutable struct Options <: AbstractOptions - schur_custom_partition::Bool = false - schur_num_parts::Int = 2 - schur_part::Vector{Int} = Int[] - schur_subproblem_solver::Module - schur_dense_solver::Module -end - -mutable struct SolverWorker - V::Vector{Int} - V_0_nz::Vector{Int} - csc::SparseMatrixCSC{Float64,Int32} - csc_view::SubVector{Float64} - compl::SparseMatrixCSC{Float64,Int32} - compl_view::SubVector{Float64} - M::AbstractLinearSolver - w::Vector{Float64} -end - -mutable struct Solver <: AbstractLinearSolver - csc::SparseMatrixCSC{Float64,Int32} - inds::Vector{Int} - tsp::TwoStagePartition - - schur::Matrix{Float64} - colors - fact - - V_0::Vector{Int} - csc_0::SparseMatrixCSC{Float64,Int32} - csc_0_view::SubVector{Float64} - w_0::Vector{Float64} - - sws::Vector{SolverWorker} - - opt::Options - logger::Logger -end - - -function Solver(csc::SparseMatrixCSC{Float64}; - option_dict::Dict{Symbol,Any}=Dict{Symbol,Any}(), - opt=Options(schur_subproblem_solver=default_linear_solver(), - schur_dense_solver=default_dense_solver()), - logger=Logger(), - kwargs...) - - set_options!(opt,option_dict,kwargs...) - if string(opt.schur_subproblem_solver) == "MadNLP.Mumps" - @warn(logger,"When Mumps is used as a subproblem solver, Schur is run in serial.") - @warn(logger,"To use parallelized Schur, use Ma27 or Ma57.") - end - - inds = collect(1:nnz(csc)) - tsp = TwoStagePartition(csc,opt.schur_part,opt.schur_num_parts) - - V_0 = findall(tsp.part.==0) - colors = get_colors(length(V_0),opt.schur_num_parts) - - csc_0,csc_0_view = get_cscsy_view(csc,V_0,inds=inds) - schur = Matrix{Float64}(undef,length(V_0),length(V_0)) - - w_0 = Vector{Float64}(undef,length(V_0)) - - sws = Vector{Any}(undef,opt.schur_num_parts) - - copied_option_dict = copy(option_dict) - @blas_safe_threads for k=1:opt.schur_num_parts - sws[k] = SolverWorker( - tsp,V_0,csc,inds,k,opt.schur_subproblem_solver,logger,k==1 ? option_dict : copy(copied_option_dict)) - end - fact = opt.schur_dense_solver.Solver(schur) - return Solver(csc,inds,tsp,schur,colors,fact,V_0,csc_0,csc_0_view,w_0,sws,opt,logger) -end - -get_colors(n0,K) = [findall((x)->mod(x-1,K)+1==k,1:n0) for k=1:K] - -function SolverWorker(tsp,V_0,csc::SparseMatrixCSC{Float64},inds::Vector{Int},k, - SubproblemSolverModule::Module,logger::Logger,option_dict::Dict{Symbol,Any}) - - V = findall(tsp.part.==k) - - csc_k,csc_k_view = get_cscsy_view(csc,V,inds=inds) - compl,compl_view = get_csc_view(csc,V,V_0,inds=inds) - V_0_nz = findnz(compl.colptr) - - M = length(V) == 0 ? - EmptyLinearSolver() : SubproblemSolverModule.Solver(csc_k;option_dict=option_dict,logger=logger) - w = Vector{Float64}(undef,csc_k.n) - - return SolverWorker(V,V_0_nz,csc_k,csc_k_view,compl,compl_view,M,w) -end - -function findnz(colptr) - nz = Int[] - for j=1:length(colptr)-1 - colptr[j]==colptr[j+1] || push!(nz,j) - end - return nz -end - -function factorize!(M::Solver) - M.schur.=0. - M.csc_0.nzval.=M.csc_0_view - M.schur.=M.csc_0 - @blas_safe_threads for sw in M.sws - sw.csc.nzval.=sw.csc_view - sw.compl.nzval.=sw.compl_view - factorize!(sw.M) - end - - # asymchronous multithreading doesn't work here - for q = 1:length(M.colors) - @blas_safe_threads for k = 1:length(M.sws) - for j = M.colors[mod(q+k-1,length(M.sws))+1] # each subprob works on a different color - factorize_worker!(j,M.sws[k],M.schur) - end - end - end - factorize!(M.fact) - - return M -end - -function factorize_worker!(j,sw,schur) - j in sw.V_0_nz || return - sw.w.= view(sw.compl,:,j) - solve!(sw.M,sw.w) - mul!(view(schur,:,j),sw.compl',sw.w,-1.,1.) -end - - -function solve!(M::Solver,x::AbstractVector{Float64}) - M.w_0 .= view(x,M.V_0) - @blas_safe_threads for sw in M.sws - sw.w.=view(x,sw.V) - solve!(sw.M,sw.w) - end - for sw in M.sws - mul!(M.w_0,sw.compl',sw.w,-1.,1.) - end - solve!(M.fact,M.w_0) - view(x,M.V_0).=M.w_0 - @blas_safe_threads for sw in M.sws - x_view = view(x,sw.V) - sw.w.= x_view - mul!(sw.w,sw.compl,M.w_0,1.,1.) - solve!(sw.M,sw.w) - x_view.=sw.w - end - return x -end - -is_inertia(M::Solver) = is_inertia(M.fact) && is_inertia(M.sws[1].M) -function inertia(M::Solver) - numpos,numzero,numneg = inertia(M.fact) - for k=1:M.opt.schur_num_parts - _numpos,_numzero,_numneg = inertia(M.sws[k].M) - numpos += _numpos - numzero += _numzero - numneg += _numneg - end - return (numpos,numzero,numneg) -end - -function improve!(M::Solver) - for sw in M.sws - improve!(sw.M) || return false - end - return true -end - -function introduce(M::Solver) - for sw in M.sws - sw.M isa EmptyLinearSolver || return "schur equipped with "*introduce(sw.M) - end -end - -end # module diff --git a/lib/MadNLPGraph/src/schwarz.jl b/lib/MadNLPGraph/src/schwarz.jl deleted file mode 100644 index e2d40ad4..00000000 --- a/lib/MadNLPGraph/src/schwarz.jl +++ /dev/null @@ -1,177 +0,0 @@ -module MadNLPSchwarz - -import ..MadNLPGraph: - @kwdef, Logger, @debug, @warn, @error, - default_linear_solver,SparseMatrixCSC, SubVector, get_cscsy_view, nnz, - SymbolicException,FactorizationException,SolveException,InertiaException, - AbstractOptions, AbstractLinearSolver, set_options!, - MonolevelPartition, MonolevelStruc, BilevelPartition, BilevelStruc, - expand!, get_current_V, get_current_size, get_full_size, - EmptyLinearSolver, introduce, factorize!, solve!, improve!, is_inertia, inertia, - set_blas_num_threads, blas_num_threads, @blas_safe_threads, @sprintf - -const INPUT_MATRIX_TYPE = :csc - -@kwdef mutable struct Options <: AbstractOptions - schwarz_num_parts_upper::Int = 0 - schwarz_num_parts::Int = 2 - schwarz_subproblem_solver::Module - schwarz_fully_improve_subproblem_solver::Bool=true - schwarz_max_expand_factor::Int = 4 - schwarz_custom_partition::Bool = false - schwarz_part_upper::Vector{Int} = Int[] - schwarz_part::Vector{Int} = Int[] -end - -mutable struct SolverWorker - struc::Union{MonolevelStruc,BilevelStruc} - - max_size::Float64 - overlap_increase_factor::Float64 - restrictor::Vector{Int} - - csc::SparseMatrixCSC{Float64,Int32} - csc_view::SubVector{Float64} - - M::AbstractLinearSolver - - p::AbstractVector{Float64} - q::AbstractVector{Float64} - q_restricted::AbstractVector{Float64} -end - -mutable struct Solver <: AbstractLinearSolver - partition::Union{MonolevelPartition,BilevelPartition} - - csc::SparseMatrixCSC{Float64} - inds::Vector{Int} - - p::Vector{Float64} - w::Vector{Float64} - sws::Vector{SolverWorker} - opt::Options - logger::Logger -end - - -function Solver(csc::SparseMatrixCSC{Float64}; - option_dict::Dict{Symbol,Any}=Dict(), - opt=Options(schwarz_subproblem_solver=default_linear_solver()),logger=Logger(), - kwargs...) - - set_options!(opt,option_dict,kwargs...) - if string(opt.schwarz_subproblem_solver) == "MadNLP.Mumps" - @warn(logger,"When Mumps is used as a subproblem solver, Schwarz is run in serial.") - @warn(logger,"To use parallelized Schwarz, use Ma27 or Ma57.") - end - - inds = collect(1:nnz(csc)) - - p = zeros(csc.n) - w = zeros(csc.n) - - if opt.schwarz_num_parts_upper == 0 - partition = MonolevelPartition( - csc,opt.schwarz_custom_partition ? opt.schwarz_part : Int32[],opt.schwarz_num_parts) - else - partition = BilevelPartition( - csc, - opt.schwarz_custom_partition ? opt.schwarz_part : Int32[],opt.schwarz_num_parts, - opt.schwarz_custom_partition ? opt.schwarz_part_upper : Int32[],opt.schwarz_num_parts_upper) - end - - - sws=Vector{SolverWorker}(undef,opt.schwarz_num_parts_upper == 0 ? opt.schwarz_num_parts : opt.schwarz_num_parts_upper) - copied_option_dict = copy(option_dict) - @blas_safe_threads for k=1:length(sws) - sws[k] = SolverWorker( - partition,csc,inds,p,k,opt.schwarz_max_expand_factor, - opt.schwarz_subproblem_solver,opt.schwarz_fully_improve_subproblem_solver, - logger,k==1 ? option_dict : copy(copied_option_dict)) - end - - saturation = maximum(sws[k].csc.n/csc.n*100 for k=1:length(sws)) - @debug(logger,@sprintf("overlap size initialized with %3d%% saturation.\n",saturation)) - - return Solver(partition,csc,inds,p,w,sws,opt,logger) -end - -function SolverWorker( - partition,csc::SparseMatrixCSC{Float64},inds::Vector{Int},p::Vector{Float64}, - k::Int,max_expand_factor::Int,SubproblemSolverModule::Module,fully_improve_subproblem_solver::Bool, - logger::Logger,option_dict::Dict{Symbol,Any}) - struc= partition isa MonolevelPartition ? - MonolevelStruc(partition,k) : BilevelStruc(partition,k) - overlap_increase_factor = - (get_full_size(partition)/get_current_size(struc))^(1/max_expand_factor) - max_size = get_current_size(struc)*(overlap_increase_factor) - p = view(p,copy(get_current_V(struc))) - restrictor = 1:length(get_current_V(struc)) - expand!(struc,partition,max_size) - csc,csc_view = get_cscsy_view(csc,get_current_V(struc),inds=inds) - if csc.n == 0 - M = EmptyLinearSolver() - @warn(logger,"empty subproblem at partition $k") - else - M = SubproblemSolverModule.Solver(csc;option_dict = option_dict,logger=logger) - end - fully_improve_subproblem_solver && while improve!(M) end # starts with fully improved - q = Vector{Float64}(undef,length(get_current_V(struc))) - q_restricted = view(q,restrictor) - - return SolverWorker(struc,max_size,overlap_increase_factor,restrictor,csc,csc_view,M,p,q,q_restricted) -end - -function factorize!(M::Solver) - @blas_safe_threads for k=1:length(M.sws) - M.sws[k].csc.nzval.=M.sws[k].csc_view - factorize!(M.sws[k].M) - end - return M -end - -is_maximal_overlap(M::Solver) = all(sw.max_size>=M.csc.n for sw in M.sws) - - -function improve!(M::Solver) - is_maximal_overlap(M) && (@debug(M.logger,"improve quality failed.\n");return false) - - @blas_safe_threads for k=1:length(M.sws) - M.sws[k].max_size *= M.sws[k].overlap_increase_factor - expand!(M.sws[k].struc,M.partition,M.sws[k].max_size) - M.sws[k].csc,M.sws[k].csc_view = get_cscsy_view(M.csc,get_current_V(M.sws[k].struc);inds=M.inds) - if M.sws[k].csc.n == 0 - M.sws[k].M = EmptyLinearSolver() - else - M.sws[k].M = M.opt.schwarz_subproblem_solver.Solver(M.sws[k].csc;opt=M.sws[k].M.opt) - end - M.opt.schwarz_fully_improve_subproblem_solver && while improve!(M.sws[k].M) end - resize!(M.sws[k].q,length(get_current_V(M.sws[k].struc))) - end - - saturation = maximum(M.sws[k].csc.n/M.csc.n*100 for k=1:length(M.sws)); - saturation == 100. ? - @warn(M.logger,@sprintf("overlap is maximally saturated")) : - @debug(M.logger,@sprintf("overlap size increased to %3d%% saturation.\n",saturation)) - - - return true -end - -is_inertia(::Solver)=false -function inertia(M::Solver) - throw(InertiaException) -end - -function solve!(M::Solver,x::AbstractVector{Float64}) - @blas_safe_threads for k=1:length(M.sws) - M.sws[k].q.=view(x,get_current_V(M.sws[k].struc)) - solve!(M.sws[k].M,M.sws[k].q) - M.sws[k].p.=M.sws[k].q_restricted - end - x.=M.p -end - -introduce(M::Solver)="schwarz equipped with "*introduce(M.sws[1].M) - -end # module diff --git a/lib/MadNLPGraph/test/runtests.jl b/lib/MadNLPGraph/test/runtests.jl deleted file mode 100644 index ebf6b602..00000000 --- a/lib/MadNLPGraph/test/runtests.jl +++ /dev/null @@ -1,44 +0,0 @@ -using Test, Plasmo, MadNLP, MadNLPTests, MadNLPGraph - -@testset "MadNLPGraphs test" begin - - n_nodes=2 - M = 200 - d = sin.((0:M*n_nodes).*pi/100) - - graph = OptiGraph() - @optinode(graph,nodes[1:n_nodes]) - - #Node models - nodecnt = 1 - for (i,node) in enumerate(nodes) - @variable(node, x[1:M]) - @variable(node, -1<=u[1:M]<=1) - @constraint(node, dynamics[i in 1:M-1], x[i+1] == x[i] + u[i]) - @objective(node, Min, sum(x[i]^2 - 2*x[i]*d[i+(nodecnt-1)*M] for i in 1:M) + - sum(u[i]^2 for i in 1:M)) - nodecnt += 1 - end - n1 = getnode(graph,1) - @constraint(n1,n1[:x][1] == 0) - for i=1:n_nodes-1 - @linkconstraint(graph, nodes[i+1][:x][1] == nodes[i][:x][M] + nodes[i][:u][M],attach=nodes[i+1]) - end - - #First node gets initial condition - kwargs_collection = [ - ("default",Dict(:print_level=>MadNLP.ERROR)), - ("schur",Dict(:linear_solver=>MadNLPSchur, :schur_custom_partition=>true,:print_level=>MadNLP.ERROR)), - ("schwarz",Dict(:linear_solver=>MadNLPSchwarz,:schwarz_custom_partition=>true,:print_level=>MadNLP.ERROR)) - ] - for (name,kwargs) in kwargs_collection - @testset "plasmo-$name" begin - MadNLP.optimize!(graph;kwargs...); - @test MadNLP.termination_status(graph.optimizer) == MOI.LOCALLY_SOLVED - @test solcmp([0.0,0.03137979101284875,0.0627286139604959,0.09401553133948139,0.12520966673966746,0.15628023531552773,0.18719657416707308,0.21792817260043182,0.24844470223822107,0.2787160469499936], value.(getnode(graph,1)[:x][1:10])) - @test solcmp([-0.0627595821831224,-0.06269764605256355,-0.06257383491492904,-0.06238827095686386,-0.06214113730759117,-0.06183267785818605,-0.06146319702088434,-0.06103305942866446,-0.06054268957539879,-0.05999257139692946], dual.(getnode(graph,1)[:dynamics][1:10])) - @test solcmp([-1.574246826343827e-10,-1.5726901766520537e-10,-1.5695784406668639e-10,-1.5649147434189076e-10,-1.5587037679839438e-10,-1.5509517501550025e-10,-1.541666471342132e-10,-1.5308572497187857e-10,-1.5185349296508524e-10,-1.504711869423148e-10], dual.(UpperBoundRef.(getnode(graph,1)[:u][1:10]))) - @test solcmp([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0], dual.(LowerBoundRef.(getnode(graph,1)[:u][1:10]))) - end - end -end