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]> 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 >>> >>>
