B u `N–ã@sÐdgZddlmZmZddlmZddlmZmZm Z m Z m Z m Z m Z mZddlmZmZmZddlmZddlmZddlZddlZdd lmZdd lmZmZmZm Z ee @Z!e "d ¡Z#Gd d„deƒZ$dS) ÚSequentialDecompositioné)ÚPortÚArc)Ú FOQUSGraph)Ú ConstraintÚvalueÚ ObjectiveÚVarÚ ConcreteModelÚBinaryÚminimizeÚ Expression)Ú ComponentSetÚ ComponentMapÚOptions)Úidentify_variables)Úgenerate_standard_repnN)Ú iteritems)ÚnetworkxÚnetworkx_availableÚnumpyÚnumpy_availablez pyomo.networkc@seZdZdZdd„Zdd„Zdd„ZdEd d „Zd d „Zd d„Z dFdd„Z dd„Z dd„Z dd„Z dd„ZdGdd„Zdd„ZdHdd „Zd!d"„Zd#d$„Zdifd%d&„Zd'd(„Zd)d*„Zd+d,„Zd-d.„Zd/d0„Zd1d2„Zd3d4„Zd5d6„Zd7d8„Zd9d:„Zd;d<„Zd=d>„Z d?d@„Z!dAdB„Z"dCdD„Z#dS)IraB A sequential decomposition tool for Pyomo Network models The following parameters can be set upon construction of this class or via the `options` attribute. Parameters ---------- graph: `MultiDiGraph` A networkx graph representing the model to be solved. `default=None (will compute it)` tear_set: `list` A list of indexes representing edges to be torn. Can be set with a list of edge tuples via set_tear_set. `default=None (will compute it)` select_tear_method: `str` Which method to use to select a tear set, either "mip" or "heuristic". `default="mip"` run_first_pass: `bool` Boolean indicating whether or not to run through network before running the tear stream convergence procedure. `default=True` solve_tears: `bool` Boolean indicating whether or not to run iterations to converge tear streams. `default=True` guesses: `ComponentMap` ComponentMap of guesses to use for first pass (see set_guesses_for method). `default=ComponentMap()` default_guess: `float` Value to use if a free variable has no guess. `default=None` almost_equal_tol: `float` Difference below which numbers are considered equal when checking port value agreement. `default=1.0E-8` log_info: `bool` Set logger level to INFO during run. `default=False` tear_method: `str` Method to use for converging tear streams, either "Direct" or "Wegstein". `default="Direct"` iterLim: `int` Limit on the number of tear iterations. `default=40` tol: `float` Tolerance at which to stop tear iterations. `default=1.0E-5` tol_type: `str` Type of tolerance value, either "abs" (absolute) or "rel" (relative to current value). `default="abs"` report_diffs: `bool` Report the matrix of differences across tear streams for every iteration. `default=False` accel_min: `float` Min value for Wegstein acceleration factor. `default=-5` accel_max: `float` Max value for Wegstein acceleration factor. `default=0` tear_solver: `str` Name of solver to use for select_tear_mip. `default="cplex"` tear_solver_io: `str` Solver IO keyword for the above solver. `default=None` tear_solver_options: `dict` Keyword options to pass to solve method. `default={}` cKsºi|_tƒ}|_d|d<d|d<d|d<d|d<d|d<tƒ|d <d|d <d |d <d |d<d|d<d|d<d|d<d|d<d |d<d|d<d|d<d|d<d|d<i|d<| |¡dS) z@Pass kwds to update the options attribute after setting defaultsNÚgraphÚtear_setÚmipÚselect_tear_methodTÚrun_first_passÚ solve_tearsÚguessesÚ default_guessg:Œ0âŽyE>Úalmost_equal_tolFÚlog_infoÚDirectÚ tear_methodé(ÚiterLimgñh㈵øä>ÚtolÚabsÚtol_typeÚ report_diffséûÿÿÿÚ accel_minrÚ accel_maxZcplexÚ tear_solverÚtear_solver_ioÚtear_solver_options)ÚcacherÚoptionsrÚupdate)ÚselfÚkwdsr1©r5ú?/tmp/pip-unpacked-wheel-n62dbgi3/pyomo/network/decomposition.pyÚ__init__’s,  z SequentialDecomposition.__init__cCs||jd|<dS)aµ Set the guesses for the given port These guesses will be checked for all free variables that are encountered during the first pass run. If a free variable has no guess, its current value will be used. If its current value is None, the default_guess option will be used. If that is None, an error will be raised. All port variables that are downstream of a non-tear edge will already be fixed. If there is a guess for a fixed variable, it will be silently ignored. The guesses should be a dict that maps the following: Port Member Name -> Value Or, for indexed members, multiple dicts that map: Port Member Name -> Index -> Value For extensive members, "Value" must be a list of tuples of the form (arc, value) to guess a value for the expanded variable of the specified arc. However, if the arc connecting this port is a 1-to-1 arc with its peer, then there will be no expanded variable for the single arc, so a regular "Value" should be provided. This dict cannot be used to pass guesses for variables within expression type members. Guesses for those variables must be assigned to the variable's current value before calling run. While this method makes things more convenient, all it does is: `self.options["guesses"][port] = guesses` rN)r1)r3Úportrr5r5r6Úset_guesses_for­s%z'SequentialDecomposition.set_guesses_forcCs||jd<dS)a Set a custom tear set to be used when running the decomposition The procedure will use this custom tear set instead of finding its own, thus it can save some time. Additionally, this will be useful for knowing which edges will need guesses. Arguments --------- tset A list of Arcs representing edges to tear While this method makes things more convenient, all it does is: `self.options["tear_set"] = tset` rN)r1)r3Útsetr5r5r6Ú set_tear_setÔsz$SequentialDecomposition.set_tear_setrcKsR|dkr|j|f|Ž}n.|dkr8|j|f|Ždd}ntd|fƒ‚| ||¡S)z² Call the specified tear selection method and return a list of arcs representing the selected tear edges. The kwds will be passed to the method. rÚ heuristicrzInvalid method '%s')Úselect_tear_mipÚselect_tear_heuristicÚ ValueErrorÚindexes_to_arcs)r3ÚGÚmethodr4r:r5r5r6Ú tear_set_arcsçs z%SequentialDecomposition.tear_set_arcscCs<| |¡}g}x(|D] }||}| |j|d¡qW|S)a- Converts a list of edge indexes to the corresponding Arcs Arguments --------- G A networkx graph corresponding to lst lst A list of edge indexes to convert to tuples Returns: A list of arcs Úarc)Ú idx_to_edgeÚappendÚedges)r3rAÚlstÚ edge_listÚresÚeiÚedger5r5r6r@øs   z'SequentialDecomposition.indexes_to_arcsc Cs|jdrtj}t tj¡t ¡}t d¡|j  ¡|jd}|dkrT|  |¡}|  |¡}|jdrt d¡|  |¡}|j ||||dd|jd r¢t|ƒsÀt ¡}t d ||¡dSt d ¡| |¡\} } } } xò| D]ê} xä| D]Ü}|j || |d }g}x&|D]}|| |kr | |¡q Wt|||||jd |jd|jd|jd| |d }|jd}|dkr†|jf|Žqì|dkrº|jd|d<|jd|d<|jf|Žqìtd|fƒ‚qìWqâW|j  ¡t ¡}t d ||¡|jdr t |¡dS)a Compute a Pyomo Network model using sequential decomposition Arguments --------- model A Pyomo model function A function to be called on each block/node in the network r!z!Starting Sequential DecompositionrNrz"Starting first pass run of networkT)Ú use_guessesrz1Finished Sequential Decomposition in %.2f secondsz#Starting tear convergence procedure)Únodesr%r&r(r)) rAÚorderÚfunctionÚtearsr%r&r(r)ÚoutEdgesr#r"ZWegsteinr+r,zInvalid tear_method '%s')r1ÚloggerÚlevelÚsetLevelÚloggingÚINFOÚtimeÚinfor0ÚclearÚ create_graphrZcalculation_orderÚ run_orderÚlenZ scc_collectrFÚdictZsolve_tear_directZsolve_tear_wegsteinr?)r3ÚmodelrPZ old_log_levelÚstartrAr:rOÚendZsccNodesZsccEdgesZsccOrderrRÚlevZsccIndexrQrKr4r#r5r5r6Úrun s`                       zSequentialDecomposition.runNFc CsŠ| ¡}tƒ}| |¡}|jd} |jd} xV|D]L} xD| D]:} | |krZtƒ|| <|| } xJ|  t¡D]<}t| ¡ƒs€qn|rš|| krš| | || ¡|  || | |¡qnW|| ƒx| D] }|  ¡q¼W|   ¡x¨|  t¡D]š}|  ¡}t|ƒsöqàx*|j dddD]}| |¡| ¡qWx6|D].}| |¡}||||kr(| ||¡q(Wx|D]}|  ¡q`W|  ¡qàWqBWq4WdS)a> Run computations in the order provided by calling the function Arguments --------- G A networkx graph corresponding to order order The order in which to run each node in the graph function The function to be called on each block/node ignore Edge indexes to ignore when passing values use_guesses If True, will check the guesses dict when fixing free variables before calling function rrTF)Ú expr_varsÚfixedN)Ú fixed_inputsrÚ edge_to_idxr1Úcomponent_data_objectsrr]ÚsourcesÚ load_guessesÚ load_valuesÚfreerZÚdestsÚ iter_varsÚaddÚfixÚ arc_to_edgeÚ pass_values)r3rArOrPÚignorerMrfÚ fixed_outputsÚedge_maprÚdefaultrbÚunitZ fixed_insr8ÚvarrmrDÚarc_mapr5r5r6r\^sD            z!SequentialDecomposition.run_ordercs8|j}|j|j}}| ¡}|jd}||kr8tƒ||<| d¡}|dk r„| ¡s„|jdk rt||  |¡|  ¡nt d|j ƒ‚nÐ|dkrTxÄt |jƒD]¶\} } | | ¡s®qš| | ¡‰ˆdkrÂqšt| ¡ƒdkràtd|j ƒ‚|  ¡rü‡fdd„ˆDƒ} n ˆdfg} xH| D]@\‰} ||  ˆ¡t|  ¡r6| | n| ƒ} ˆ  t| ƒ¡q WqšWxÞ|jtd d D]Ì}|js~t d |j ƒ‚t|jƒ}| ¡rÆtt|jƒ|jƒ|krdt d ||||j fƒ‚qd| ¡ràt|jƒdksît d |j ƒ‚t|jƒ|j|jd} |jd}||  |¡|  t| ƒ¡qdWdS)z¤ Pass the values from one unit to the next, recording only those that were not already fixed in the provided dict that maps blocks to sets. r Ú splitfracNzÔFound free splitfrac for arc '%s' with no current value. Please use the set_split_fraction method on its source port to set this value before expansion, or set its value manually if expansion has already occured.éz-This still needs to be figured out (arc '%s')csg|]}ˆ||f‘qSr5r5)Ú.0Úi)Úevarr5r6ú Ñsz7SequentialDecomposition.pass_values..T)ÚactivezJFound inequality constraint '%s'. Please do not modify the expanded block.zfFound connected ports '%s' and '%s' both with fixed but different values (by > %s) for constraint '%s'z™Constraint '%s' had more than one free variable when trying to pass a value to its destination. Please fix more variables before passing across this arc.r) Úexpanded_blockÚsrcÚdestÚ parent_blockr1rÚ componentÚis_fixedrrorpÚ RuntimeErrorÚnamerÚvarsÚ is_extensiver]rmÚ ExceptionÚ is_indexedÚfloatrhrZequalityrÚbodyr'ÚlowerÚconstantÚ is_linearÚ linear_varsÚ linear_coefs)r3rDrfZeblockr‚rƒÚ dest_unitÚeq_tolÚsfrˆÚmemÚevarsÚidxÚvalÚconÚrepnrxr5)r~r6rršsh                z#SequentialDecomposition.pass_valuesc CsÎ|jd}| ¡r %s) than what is being passed to itr{rz’Member '%s' of port '%s' had more than one free variable when trying to pass a value to it. Please fix more variables before passing to this port.N)r1r†r'rr‡rˆÚis_expression_typerr‘r]r’rr“rorpr) r3r8rˆÚmemberršrer•rœZfvalrxr5r5r6Úpass_single_valueús$     z)SequentialDecomposition.pass_single_valuec sŽ| ¡}x~t|jƒD]n\}‰y|||‰Wntk rFwYnXtˆtƒrh‡‡fdd„ˆDƒ}n(ˆ ¡r„td||jfƒ‚n ˆˆdfg}xô|D]ì\}‰}|  ¡rªq–d} |  |¡r0xtˆD]l\} } | |kræt d||j| jfƒ‚| j   |¡} | dkrüPd} | |} |   ¡rqÀ| | ¡|  t| ƒ¡qÀW| s–| ¡rjt d|ˆ ¡rZdt|ƒnd |jfƒ‚q–| |¡| tˆƒ¡q–WqWdS) Ncsg|]}ˆ|ˆ||f‘qSr5r5)r|Úk)Úentryr—r5r6r&sz8SequentialDecomposition.load_guesses..zHGuess for indexed member '%s' in port '%s' must map to a dict of indexesFzeFound a guess for extensive member '%s' on port '%s' using arc '%s' that is not a source of this portTzzCannot provide guess for expression type member '%s%s' of port '%s', must set current value of variables within expressionz[%s]Ú)rirr‰ÚKeyErrorÚ isinstancer^rŒÚ TypeErrorrˆr†rŠr?rr…rorprrÚstr) r3rr8reZsrcsrˆZitrrxr™Z has_evarsrDršr~r5)r¡r—r6rjsR        z$SequentialDecomposition.load_guessesc s0| ¡}x |jdddD] \‰}}d}| ˆ¡r˜‡fdd„|Dƒ}|ddkrXd}n@y*x$tt|ƒƒD]} || ||| <qhWWntk r–YnX|dk râx,|D]$} |  ¡r´q¦|j|| |||ddq¦W| |ˆ|||¡q|  ¡rx:t |ddD]} | || |||¡qúWq| |||||¡qWdS) NFT)reÚnamescsg|]}|j ˆ¡‘qSr5)rr…)r|rD)rˆr5r6r\sz7SequentialDecomposition.load_values..r)Ú extensive)Z include_fixed) rirnrŠÚranger]ÚAttributeErrorr†Úcheck_value_fixÚcombine_and_fixrr) r3r8rvrerMriÚindexÚobjr˜Újr~rxr5)rˆr6rkVs2        z#SequentialDecomposition.load_valuescCsxd}|jdk r|j}n |dk r"|}|dkr\td|r6dnd|jdt|ƒ|j|rRdndfƒ‚| |¡| t|ƒ¡dS)zT Try to fix the var at its current value or the default, else error NzEncountered a free inlet %svariable '%s' %s port '%s' with no %scurrent value, or default_guess option, while attempting to compute the unit.z extensive r¢)ÚonÚtozguess, )rr‡rˆÚintrorpr)r3r8rxrvrerMr¨ršr5r5r6r«zs    z'SequentialDecomposition.check_value_fixcCs>tdd„|Dƒƒst‚tdd„|Dƒƒ}| |||||¡dS)zÁ For an extensive port member, combine the values of all expanded variables and fix the port member at their sum. Assumes that all expanded variables are fixed. css|]}| ¡VqdS)N)r†)r|r~r5r5r6ú ™sz:SequentialDecomposition.combine_and_fix..css|]}t|ƒVqdS)N)r)r|r~r5r5r6r³šsN)ÚallÚAssertionErrorÚsumrŸ)r3r8rˆr®r˜reÚtotalr5r5r6r¬“sz'SequentialDecomposition.combine_and_fixcCsL|j |¡r(|j |¡}|dk r(||S|jj|}| ¡rD||S|SdS)a Return the object that is the peer to the source port's member. This is either the destination port's member, or the variable on the arc's expanded block for Extensive properties. This will return the appropriate index of the peer. N)r‚rŠrr…rƒr‰rŒ)r3rDrˆr­r~r—r5r5r6Úsource_dest_peers   z(SequentialDecomposition.source_dest_peercCsrt ¡}xd| t¡D]V}|js,td|jƒ‚|jdkrDtd|jƒ‚|j  ¡|j   ¡}}|j |||dqW|S)a- Returns a networkx MultiDiGraph of a Pyomo network model The nodes are units and the edges follow Pyomo Arc objects. Nodes that get added to the graph are determined by the parent blocks of the source and destination Ports of every Arc in the model. Edges are added for each Arc using the direction specified by source and destination. All Arcs in the model will be used whether or not they are active (since this needs to be done after expansion), and they all need to be directed. zWAll Arcs must be directed when creating a graph for a model. Found undirected Arc: '%s'NzWAll Arcs must be expanded when creating a graph for a model. Found unexpanded Arc: '%s')rD) ÚnxZ MultiDiGraphrhrZdirectedr?rˆrr‚r„rƒZadd_edge)r3r_rArDr‚rƒr5r5r6r[°s    z$SequentialDecomposition.create_graphcs*tƒ}g‰x.)Úexprz cycle_min%sr{z_geq%siècss|] }|VqdS)Nr5)r|rxr5r5r6r³ùs)r¼Zsense)r r©Znumber_of_edgesr r rFZ add_componentZmax_cycle_tearsZ all_cyclesr]r r¶rrˆrr r®)r3rAr_r}ZvnamerxZmctÚ_Z cycleEdgesZecycZenamer¼Z cname_minZcon_minZ cname_mctZcon_mctZobj_exprr5)r»r6Úselect_tear_mip_modelÌs.      z-SequentialDecomposition.select_tear_mip_modelc Cs†| |¡\}}ddlm}|||d}|jddsBtd||fƒ‚|j|f|Žg} x,tt|ƒƒD]} || jdkrb|   | ¡qbW| S)a~ This finds optimal sets of tear edges based on two criteria. The primary objective is to minimize the maximum number of times any cycle is broken. The seconday criteria is to minimize the number of tears. This function creates a MIP problem in Pyomo with a doubly weighted objective and solves it with the solver arguments. r)Ú SolverFactory)Ú solver_ioF)Zexception_flagzKSolver '%s' (solver_io=%r) is not available, please pass a different solverr{) r¾Z pyomo.environr¿Ú availabler?Zsolver©r]rrF) r3rAZsolverrÀZsolver_optionsr_r»r¿Úoptr:r}r5r5r6r=þs     z'SequentialDecomposition.select_tear_mipcCsž|dkrtd|fƒ‚||}|dkr,|}nntjddd}||}tjf|Žd|t |¡<tt |¡ƒršx.tt|ƒƒD]}t ||¡rx||||<qxW|S)z?Compute the diff between svals and dvals for the given tol_type)r'ÚrelzInvalid tol_type '%s'r'rs)ÚdivideÚinvalidr)r?rZseterrÚisnanÚanyÚisinfr©r])r3ÚsvalsÚdvalsr(ZdiffÚerrZ old_settingsr}r5r5r6Ú compute_errs z#SequentialDecomposition.compute_errc CsÐg}g}| |¡}x |D]˜}|j||d}|j|j}} |j d¡} xf|jddD]V\} } } | | ¡r†| dk r†| t | | ƒ¡n| t | ƒ¡| t |  || | ¡ƒ¡qVWqWt   |¡}t   |¡}||fS)z‚ Returns numpy arrays of values for src and dest members for all edges in the tears list of edge indexes. rDrzT)r§N) rErGr‚rƒrr…rnrŠrFrr¸rÚarray)r3rArQrÉrÊrIÚtearrDr‚rƒr–rˆr­r—r5r5r6Útear_diff_direct0s      z(SequentialDecomposition.tear_diff_directcCsŽtƒ}| |¡}xx|D]p}|j||d}x*|jjdddD]}| |¡| ¡q>W| || ¡¡x|D] }|  ¡qnW|  ¡qWdS)z+Call pass values for a list of edge indexesrDTF)rdreN) rrErGr‚rnrorprrrfrlrZ)r3rArGrtrIrKrDrxr5r5r6Ú pass_edgesGs      z"SequentialDecomposition.pass_edgescCstƒ}| |¡}xz|D]r}|j||d}x*|jjdddD]}| |¡| ¡q>W|j|| ¡dx|D] }|  ¡qpW|  ¡qWdS)z2Pass values across all tears in the given tear setrDTF)rdre)rfN) rrErGr‚rnrorprrrfrlrZ)r3rArQrtrIrÎrDrxr5r5r6Úpass_tear_directVs      z(SequentialDecomposition.pass_tear_directc Cs®| ¡}| |¡}d}x’|D]Š}|j||d}|j|j} } |  ¡} | |krZtƒ|| <xJ| jddD]:\} } }| || | ¡}|  | | ||||| ¡|d7}qhWqWdS)zv Set the destination value of all tear edges to the corresponding value in the numpy array x. rrDT)r§r{N) rfrErGr‚rƒr„rrnr¸rŸ)r3rArQÚxrfrIr}rÎrDr‚rƒr”rˆr­r—Úpeerr5r5r6Úpass_tear_wegsteinfs    z*SequentialDecomposition.pass_tear_wegsteinc Csž| |¡}g}x€|D]x}|j||d}|j}|j d¡}xN|jddD]>\} } } | | ¡rz|dk rz| t| |ƒ¡qJ| t| ƒ¡qJWqWt   |¡}|S)NrDrzT)r§) rErGr‚rr…rnrŠrFrrrÍ) r3rArQrIZgofxrÎrDr‚r–rˆr­r—r5r5r6Ú generate_gofx|s    z%SequentialDecomposition.generate_gofxc Csv| |¡}g}xX|D]P}|j||d}x8|jjddD]&\}}} | |||¡} | t| ƒ¡q:WqWt |¡}|S)NrDT)r§) rErGr‚rnr¸rFrrrÍ) r3rArQrIrÒrÎrDrˆr­r—rÓr5r5r6Úgenerate_first_xŒs   z(SequentialDecomposition.generate_first_xcGs*||jkr|j|S||Ž}||j|<|S)N)r0)r3ÚkeyÚfcnÚargsrJr5r5r6Úcacher—s    zSequentialDecomposition.cachercs d‰‡‡fdd„}ˆ ˆ||¡S)Nrcs̈jˆ}|dk rlˆ |¡}ˆ |¡}g}x|D]}| |||¡q0Wˆ ||¡s^tdƒ‚|ˆjˆ<|Sˆjd}|dkr ˆ |ˆjdˆjdˆjd¡S|dkrºˆ |¡ddStd |fƒ‚dS) Nz:Tear set found in options is insufficient to solve networkrrr-r.r/r<rzInvalid select_tear_method '%s') r1rqrgrFZcheck_tear_setr?r0r=r>)rAr:ryrurJrDrB)r×r3r5r6rØ s(        z-SequentialDecomposition.tear_set..fcn)rÚ)r3rArØr5)r×r3r6ržsz SequentialDecomposition.tear_setcCsdd„}| d||¡S)z0Returns a mapping from arcs to edges for a graphcSs0tƒ}x$|jD]}|j|d}|||<qW|S)NrD)rrG)rArJrLrDr5r5r6rؽs   z0SequentialDecomposition.arc_to_edge..fcnrq)rÚ)r3rArØr5r5r6rq»sz#SequentialDecomposition.arc_to_edgecCs | dt¡S)Nrf)rÚr^)r3r5r5r6rfÅsz$SequentialDecomposition.fixed_inputscCs| dt|j¡S)z3Returns a mapping from indexes to nodes for a graphÚ idx_to_node)rÚÚlistrN)r3rAr5r5r6rÛÈsz#SequentialDecomposition.idx_to_nodecCsdd„}| d||¡S)z3Returns a mapping from nodes to indexes for a graphcSs.tƒ}d}x|jD]}|d7}|||<qW|S)Néÿÿÿÿr{)r^rN)rArJr}Únoder5r5r6rØÎs   z0SequentialDecomposition.node_to_idx..fcnÚ node_to_idx)rÚ)r3rArØr5r5r6rßÌsz#SequentialDecomposition.node_to_idxcCs| dt|j¡S)z3Returns a mapping from indexes to edges for a graphrE)rÚrÜrG)r3rAr5r5r6rE×sz#SequentialDecomposition.idx_to_edgecCsdd„}| d||¡S)z3Returns a mapping from edges to indexes for a graphcSs.tƒ}d}x|jD]}|d7}|||<qW|S)NrÝr{)r^rG)rArJr}rLr5r5r6rØÝs   z0SequentialDecomposition.edge_to_idx..fcnrg)rÚ)r3rArØr5r5r6rgÛsz#SequentialDecomposition.edge_to_idx)r)NF)F)N)$Ú__name__Ú __module__Ú __qualname__Ú__doc__r7r9r;rCr@rcr\rrrŸrjrkr«r¬r¸r[r¾r=rÌrÏrÐrÑrÔrÕrÖrÚrrqrfrÛrßrErgr5r5r5r6r sBp' Q <`#9%  2   )%Ú__all__Z pyomo.networkrrZpyomo.network.foqus_graphrZ pyomo.corerrrr r r r r Zpyomo.common.collectionsrrrZpyomo.core.expr.currentrZ pyomo.repnrrVrXÚsixrZpyomo.common.dependenciesrr¹rrrZimports_availableÚ getLoggerrSrr5r5r5r6Ú s (