I understand and concur with your comment regarding the proper use of abstract types. And yes, defining appropriate abstractions is challenging. A few more iterations will likely be required for us. For now, most of my hesitations concern how to extend a given object and its functionality: say using the scheme above or, and I am now only starting to experiment with this, using the hooks provided by the base type. In the example above, we could have proceeded by using the *JuMP* Model *ext* dictionary to store a data structure providing what we need to pair with the JuMP model, ( i.e.: a demand scenario, initial conditions, a list of contingencies, etc), as is more or less done in the *JuMPStoch* and *JuMPeR* modules. But it is not clear to me how we could then use polymorphism on the calls to *setGenCapa!, setRamping!*, etc. Perhaps I should look more closely at the implementation of *MathProgBase*' solver interface.
Thanks again for your comments! Le jeudi 11 juin 2015 15:45:40 UTC-5, Miles Lubin a écrit : > > There's a lot going on here, but a few observations: > > - In the (unreleased) development version of JuMP, we store a dictionary > of variables used in the model ( > https://github.com/JuliaOpt/JuMP.jl/pull/446), so you won't need to > manually keep track of this e.g., with the "var" dictionary above. > - That said, there's a bit of a performance penalty for going through > dictionaries. Don't worry about this until it becomes a bottleneck, though. > - Abstract types specify what do can "do with" a certain object, but not > what fields are available in an object. So it's a bit of poor style to > access the fields of an object in generic code (in setLineCapa!, you > access m.mip, but m is not guaranteed to have this field). I admit to > doing this myself sometimes, though. The suggested workaround is to define > accessor methods like mip(m::Model) = m.mip. Then the "delegation/proxy" > pattern above is replaced by defining new methods, e.g., mip(m::Model) = > m.uc.mip. > > Does this help? It's always a challange to design good abstractions, and > we're definitely open to making it easier to build modular models like this > in JuMP. Are there Julia language features which you feel are making this > more difficult than you expect? > > Best, > Miles > > On Thu, Jun 4, 2015 at 10:28 PM, Francois Gilbert <[email protected] > <javascript:>> wrote: > >> >> Hi Miles, >> >> It would be easier for me to give some excerpts of the Julia sources I am >> working with. I think it would suffice to illustrate the mechanic we are >> trying to implement. >> >> Say we have two abstract types: >> >> abstract BasicUC >> abstract CCModel<:BasicUC >> >> These are declared in separate modules. BasicUC stands for basic >> unit-commitment and types that derive it have access to functionalities >> such as: >> >> function setLineCapa!(m::BasicUC) >> JuMP.@addConstraint(m.mip, _contraint1[l = keys(m.net.branch), t >> = m.timeIndex], >> m.var[:flow][l,t] <= m.net.branch[l].Fmax) >> ... >> end >> >> CCModel stands for an extended unit-commitment formulation, where robust >> reserve requirement are implemented. Details are irrelevant. A type that >> derives CCModel has access to, say: >> >> function setRobustReserve!(m::CCModel) >> ... >> end >> >> Extending the functionality specific to BasicUC - let a type “Model” such >> that >> >> type Model{T<:BasicUC} <: CCModel >> uc # Hook to BasicUC >> # Proxies to BasicUC: >> demand # exogenous stuff >> mip # JuMP model >> . . . >> function Model(net,scenario) >> this=new() >> this.uc=T(net,scenario) >> . . . >> # Delegation to BasicUC >> this.demand=this.uc.demand >> this.mip=this.uc.mip >> this.var=this.uc.var >> . . . >> # Specific to CCModel >> this.contingencies=scenario[:contingencies] >> JuMP.@defVar(this.mip, _post_gen[keys(this.net.gen), >> keys(this.contingencies), >> this.timeIndex]) >> this.var[:post_gen]= _post_gen >> . . . >> return this >> end >> end >> >> Now, let a concrete type "BasicModel" be defined in, say module >> "BasicUnitCommitment", and such that : >> >> BasicModel<:BasicUC >> >> Type Model{T} above can then be instantiated and tailored, with: >> >> m=Model{BasicUnitCommitment.BaseModel}(net,scenario) >> # from the base model (BasicUC) >> setBalanceEq!(m) >> setLineCapa!(m) >> setInitRamping!(m) >> # these are overloaded by CCModel >> setObjective!(m) >> setGenCapa!(m) >> setRamping!(m) >> # these are specific to CCModel >> setPostBalanceEq!(m) >> setRobustReserve!(m) >> . . . >> >> So that's the idea. Perhaps I am overdoing this, but I think having some >> kind of encapsulation is essential for developing larger applications. >> Emulating inheritance, as is done above, is rather straightforward, but >> perhaps a little laborious, and there might be a better way to go about >> it. >> >> François >> >> >> >> >> >> Le mercredi 3 juin 2015 16:12:21 UTC-5, Miles Lubin a écrit : >>> >>> Hi Francois, >>> >>> Could you give an example of how you might organize this code in a more >>> namespace heavy language like C++ or Java? I think this is quite a general >>> question on how to structure large Julia applications, not really specific >>> to JuMP. >>> >>> Best, >>> Miles >>> >>> On Wednesday, June 3, 2015 at 9:07:03 PM UTC+2, Francois Gilbert wrote: >>>> >>>> Hi all, >>>> >>>> Is anyone aware of any effort building large OR applications? I am >>>> currently working at organizing a relatively large code base making use of >>>> the JuMP libraries. The context is unit-commitment and transmission >>>> planning. Flexibility and code re-use is among our priorities. >>>> >>>> Here are some of the design choices we made so far: >>>> >>>> 1) JuMP models are encapsulated in types that hold: >>>> a) references to the variable names, constraints (when multipliers are >>>> needed) and indices. These are otherwise difficult to extract directly >>>> from the JuMP object >>>> b) data structure that are required for model specification (generator >>>> characteristics, transmission lines, etc) >>>> c) exogenous variable values (demand scenarios, initial conditions, etc) >>>> >>>> 2) Most models we use are derived from a basic unit commitment model. >>>> The derivation is implemented using >>>> composition: the derived type manages a reference to the base type, and >>>> provides a number of proxies to some of the base type data structure. >>>> >>>> 3) The objective and constraints are specified through function >>>> calls, using polymorphism on the basis of an underlying abstract types >>>> hierarchy. >>>> >>>> I also toyed for a while with submodules to incorporate functionality >>>> that only make sense within the scope of a given base module. But from the >>>> language point of view, I did not see much benefit in doing so. That is, >>>> a >>>> Julia submodule does not see anything of the base module, unless it is >>>> explicitly use/imported, and is thus in the same position as any other >>>> module defined outside the scope of the base module. Are there any Julia >>>> packages making use submodules? >>>> >>>> Any suggestions/comments are very welcome, >>>> >>>> François >>>> >>>> >
