B _@sdZddlZddlZddlmZmZddlmZmZddl m Z ddl m Z ddl mZddlmZdd lmZmZmZmZmZmZmZmZmZmZmZmZmZmZm Z m!Z!dd l"m#Z#dd l$m%Z%m&Z&m'Z'dd l(m)Z)m*Z*dd l+m,Z,m-Z-ddl.m/m0m1Z2ddl3m4Z4m5Z5m6Z6ddl7m8Z8m9Z9m:Z:m;Z;mZ>m?Z?m@Z@mAZAddlBmCZCddlDmEZEddlFmGZGmHZHddlImJZKeLdZMiZNddZOe&jPdddGddde%ZQdS)z@Big-M Generalized Disjunctive Programming transformation module.N) ComponentMap ComponentSet) ConfigBlock ConfigValue)unique_component_name) deprecated)compute_bounds_on_expr)inf)Block BooleanVar Connector ConstraintParamSetSetOfSuffixVar ExpressionSortComponentsTraversalStrategyvalueRangeSetNonNegativeIntegersLogicalConstraint)ExternalFunction)TransformationTransformationFactory Reference) ComponentUIDActiveComponent) ConcreteModel AbstractModel)Disjunct Disjunction GDP_Error) #_warn_for_active_logical_constraint target_list is_child_ofget_src_disjunctionget_src_constraintget_transformed_constraints_get_constraint_transBlockget_src_disjunct_warn_for_active_disjunction_warn_for_active_disjunct)generate_standard_repn)wraps)iterkeys iteritems)refzpyomo.gdp.bigmcCst|ttfr|Sd|iS)N) isinstancedictr)valr7:/tmp/pip-unpacked-wheel-d4p3hk07/pyomo/gdp/plugins/bigm.py_to_dict0sr9zgdp.bigmz*Relax disjunctive model using big-M terms.)docc seZdZdZedZedededddedede d d ded ed e d ddfddZ dRddZ ddZ ddZddZddZddZddZdd Zd!d"ZdSd#d$Zd%d&Zd'd(Zd)d*Zd+d,Zd-d.Zd/d0Zd1d2Zd3d4Zd5d6Zd7d8ZdTd9d:Z d;d<Z!d=d>Z"d?d@Z#e$e%dAdBZ%e$e&dCdDZ&e$e'dEdFZ'e$e(dGdHZ(e)dIdJdKdLdMZ*dNdOZ+dPdQZ,Z-S)UBigM_TransformationaRelax disjunctive model using big-M terms. Relaxes a disjunctive model into an algebraic model by adding Big-M terms to all disjunctive constraints. This transformation accepts the following keyword arguments: bigM: A user-specified value (or dict) of M values to use (see below) targets: the targets to transform [default: the instance] M values are determined as follows: 1) if the constraint appears in the bigM argument dict 2) if the constraint parent_component appears in the bigM argument dict 3) if any block which is an ancestor to the constraint appears in the bigM argument dict 3) if 'None' is in the bigM argument dict 4) if the constraint or the constraint parent_component appear in a BigM Suffix attached to any parent_block() beginning with the constraint's parent_block and moving up to the root model. 5) if None appears in a BigM Suffix attached to any parent_block() between the constraint and the root model. 6) if the constraint is linear, estimate M using the variable bounds M values may be a single value or a 2-tuple specifying the M for the lower bound and the upper bound of the constraint body. Specifying "bigM=N" is automatically mapped to "bigM={None: N}". The transformation will create a new Block with a unique name beginning "_pyomo_gdp_bigm_reformulation". That Block will contain an indexed Block named "relaxedDisjuncts", which will hold the relaxed disjuncts. This block is indexed by an integer indicating the order in which the disjuncts were relaxed. Each block has a dictionary "_constraintMap": 'srcConstraints': ComponentMap(: ) 'transformedConstraints': ComponentMap(: ) All transformed Disjuncts will have a pointer to the block their transformed constraints are on, and all transformed Disjunctions will have a pointer to the corresponding OR or XOR constraint. zgdp.bigmtargetsNz.target or list of targets that will be relaxedaJ This specifies the list of components to relax. If None (default), the entire model is transformed. Note that if the transformation is done out of place, the list of targets should be attached to the model before it is cloned, and the list will specify the targets on the cloned instance.)defaultdomain descriptionr:bigMz*Big-M value used for constraint relaxationz A user-specified value, dict, or ComponentMap of M values that override M-values found through model Suffixes or that would otherwise be calculated using variable domains.assume_fixed_vars_permanentFzBoolean indicating whether or not to transform so that the the transformed model will still be valid when fixed Vars are unfixed.a This is only relevant when the transformation will be estimating values for M. If True, the transformation will calculate M values assuming that fixed variables will always be fixed to their current values. This means that if a fixed variable is unfixed after transformation, the transformed model is potentially no longer valid. By default, the transformation will assume fixed variables could be unfixed in the future and will use their bounds to calculate the M value rather than their value. Note that this could make for a weaker LP relaxation while the variables remain fixed. cs^tt|t|jtdtdtdtdt dt dt dt dt dt|jt|jt|jt|jtdi|_dS)z!Initialize transformation object.FN)superr;__init__r _transform_constraintrr r rrrrrrr#r-r"r.r _transform_block_on_disjunctr"_warn_for_active_logical_statementrhandlers)self) __class__r7r8rCs zBigM_Transformation.__init__cCsBg}|}x4||k r<|d}t|tkr2|||}q W|S)NZBigM) componenttyperappend parent_block)rHblockstopping_block suffix_listZ orig_blockbigmr7r7r8_get_bigm_suffix_lists     z)BigM_Transformation._get_bigm_suffix_listcCsDg}|dkr|Sx.|dk r>||kr4||||i|}qW|S)N)rLrM)rHZ bigm_argsrNarg_listr7r7r8_get_bigm_arg_lists  z&BigM_Transformation._get_bigm_arg_listcKs<trtt|_z|j|f|Wdt|jXdS)N) NAME_BUFFERAssertionErrorr used_args_apply_to_implclear)rHinstancekwdsr7r7r8 _apply_tos zBigM_Transformation._apply_toc Ks||di}d|kr0td|d|_|||j}|j|_|j}|dkr\|f}i}x|D]}t|||dst d|j |j fqf|j t kr| r|||n||||qf|j ttfkr| r|||n |||qft d|j t|fqfW|dk rt|t|j}t|dkrd} x6|D].} t| d rj| d | j 7} n | d | 7} qJWt| dS) NoptionsZ default_bigMzEDEPRECATED: the 'default_bigM=' argument has been replaced by 'bigM=')parentchild knownBlocksz0Target '%s' is not a component on instance '%s'!zbTarget '%s' was not a Block, Disjunct, or Disjunction. It was of type %s and can't be transformed.rzWUnused arguments in the bigM map! These arguments were not used by the transformation: namez %s )CONFIGpoploggerwarnr@ set_valuerAr<r'r$ractyper# is_indexed_transform_disjunction_transform_disjunctionDataindexr r"_transform_block_transform_blockDatarKrkeysrWlenhasattr) rHrZr[configr@r<r`tZ unused_argsZ warning_msgrJr7r7r8rXsL         z"BigM_Transformation._apply_to_implcCs:t|d}t}|||tt|_tddgd|_|S)NZ_pyomo_gdp_bigm_reformulationlbub)Z initialize)rr add_componentrrelaxedDisjunctsrlbub)rHrZZtransBlockName transBlockr7r7r8_add_transformation_blocks  z-BigM_Transformation._add_transformation_blockcCs*x$tt|D]}||||qWdS)N)sortedr1rm)rHobjr@ir7r7r8rl%sz$BigM_Transformation._transform_blockcCs6x0|jtdtjttftjdD]}|||qWdS)NT)activesort descend_intoZ descent_order) component_objectsr#r deterministicr r"rZ PostfixDFSri)rHr{r@ disjunctionr7r7r8rm)sz(BigM_Transformation._transform_blockDatacCspt|tst|jdk r |S|r4t|nt}d}t||jdt d|}| ||t ||_|S)NZ_xorT)fully_qualified name_buffer) r4r#rV_algebraic_constraintrhr index_setrgetnamerUru weakref_ref)rHrrxZorCnmZorCnamer7r7r8_add_xor_constraint3s   z'BigM_Transformation._add_xor_constraintcCsf|js dS|jdk r"|}n||}x(tt|D]}||||||q>W|dS)N)r}rrMryrzr1rj deactivate)rHr{r@rxr|r7r7r8riQs z*BigM_Transformation._transform_disjunctionc Cs|js dS|dkr@|jdk r2|}n||}|||}|j}d}t|jdkr~t d|j dt dx@|jD]6}||j 7}| |} |||} ||||| | qW|r|dk||<n |dk||<t|||_|dS)NrzIDisjunction '%s' is empty. This is likely indicative of a modeling error.T)rr)r}parent_componentrrMryrxorroZ disjunctsr$rrU indicator_varrRrT_transform_disjunctrr) rHr{r@rkrxZ xorConstraintrZor_exprdisjunctrPrSr7r7r8rjds2         z.BigM_Transformation._transform_disjunctionDatacCs|jsT|jr:t|jdkr"dStd|jt|jf|jdkrTtd|jf|jdk rltd|j|j}|t|}i|_ t |_ t ||_t ||_ |||||||dS)Nrz\The disjunct '%s' is deactivated, but the indicator_var is fixed to %s. This makes no sense.zThe disjunct '%s' is deactivated, but the indicator_var is not fixed and the disjunct does not appear to have been relaxed. This makes no sense. (If the intent is to deactivate the disjunct, fix its indicator_var to 0.)zThe disjunct '%s' has been transformed, but a disjunction it appears in has not. Putting the same disjunct in multiple disjunctions is not supported.)r}rZis_fixedrr$ra_transformation_blockrvrobigm_srcr localVarReferencesr _srcDisjunct_transform_block_componentsZ$_deactivate_without_fixing_indicator)rHr{rxr@rSrPrvZrelaxationBlockr7r7r8rs.        z'BigM_Transformation._transform_disjunctc Cs|}|j}x8|jttddD]$}|t||jdtdt |q W| } x>|j t t jtdD](} | jdkrtqd|  } || | qdWxR|jdddD]@} |j| jd} | s| dkrtd| jq| | ||||qWdS)N)rr}T)rr)r~rF)r}rzNo BigM transformation handler registered for modeling components of type %s. If your disjuncts contain non-GDP Pyomo components that require transformation, please transform them first.)rrrrr rurrrUrrMZcomponent_data_objectsr#rrZalgebraic_constraint_transfer_transBlock_datarGgetrgr$) rHrNrr@rSrP disjunctBlockZ varRefBlockvZdestinationBlockr{rxhandlerr7r7r8rs0    z/BigM_Transformation._transform_block_componentsc Csx|j}g}xRt|jD]D\}}|t|}|||}t||_t||_||qWx|D] }|j|=qdWdS)N)rvr2roZtransfer_attributes_fromrrrrL) rHZ fromBlockZtoBlockZ disjunctListZ to_deleteidxrZnewblockoriginalr7r7r8r s     z-BigM_Transformation._transfer_transBlock_datacCst||tdS)N)r-rU)rHrrbigMargsrSrPr7r7r8r-'sz0BigM_Transformation._warn_for_active_disjunctioncCst||tdS)N)r.rU)rHZ innerdisjunctZ outerdisjunctrrSrPr7r7r8r.+sz-BigM_Transformation._warn_for_active_disjunctcCst||tdS)N)r%rU)rHZlogical_statmentrZinfodictrrPr7r7r8rF/sz6BigM_Transformation._warn_for_active_logical_statementcCs0x*tt|D]}|||||||qWdS)N)rzr1r)rHrNrrrSrPr|r7r7r8rE3sz0BigM_Transformation._transform_block_on_disjunctcCs t|dsttd|_|jS)N_constraintMap)srcConstraintstransformedConstraints)rprr)rHrxr7r7r8_get_constraint_map_dict>s  z,BigM_Transformation._get_constraint_map_dictc Cstt|ttfsP|dkrd}n4y| |f}Wn$td|t|fYnXt|dkrptdt||f|S)N)NNzHError converting scalar M-value %s to (-M,M). Is %s not a numeric type?zuBig-M %s for constraint %s is not of length two. Expected either a single value or tuple or list of length two for M.) r4tuplelistrderrorrKror$str)rHMconstraint_namer7r7r8_convert_M_to_tupleEs z'BigM_Transformation._convert_M_to_tuplecCs|}|j}||}|} |jdtd} t|| } |r^t| | j } | |d|<n t| j } | | | ||d| <xXt t |D]F} || }|jsqd}d}||||||\}}|d|df}ttjr|jdtd}td| t|f|ddkr |jdk s:|ddkrz|jdk rz|j||d }|||||||\}}|d|df}ttjr|jdtd}td | t|f|jdk r|ddkr||j| d|j|df}|dddf}|jdk r<|ddkr<|d||j| d|jf}|dddf}ttjrn|jdtd}td | t|f||f||<| jtkr| d }| d }n$|r| df}| df}nd}d}|jdk r.|ddkrtd| |dd|j }| !||j|j|k| |g|d|<||d| |<|jdk r|ddkrTtd| |dd|j }| !||j||jk|d"|}|dk r|d|#| |n| |g|d|<||d| |<|$qWdS)NT)rrrr)NNNrzLGDP(BigM): The value for M for constraint '%s' from the BigM argument is %s.r)rOzMGDP(BigM): The value for M for constraint '%s' after checking suffixes is %s.zRGDP(BigM): The value for M for constraint '%s' after estimating (if needed) is %s.)rs)rtrsrtzBCannot relax disjunctive constraint '%s' because M is not defined.)%rrrrMrrUrrhr rrwrurzr1r}_get_M_from_argsrd isEnabledForloggingDEBUGdebugrlowerupperrRextend_update_M_from_suffixes _estimate_MbodyrIrr$raddrrLr)rHr{rrrSZdisjunct_suffix_listrxrZ constraintMapZdisjunctionRelaxationBlockZ cons_nameraZ newConstraintr|crrr_namerPZi_lbZi_ubZM_exprZ transformedr7r7r8rDZs                       z)BigM_Transformation._transform_constraintc Csx|||}|r<|ddk r<| r*||j|<|d||f}d}|rl|ddk rl| rZ||j|<|d||f}d}||||fS)NrFr)rrW) rHmrr need_lower need_uppersrckeyr from_argsr7r7r8_process_M_values   z$BigM_Transformation._process_M_valuec Csn|dkr||fS|jdk }|jdk }|jdtd}|} ||kr||} |j| |||||||dd \}}}}|s|s||fSnD| |kr|| } |j| |||||| |dd \}}}}|s|s||fSxV|D]N} xHt| D]<\} } |j| |||||| |dd \}}}}|s|s||fSqWqWd|krf|d} |j| |||||d|dd \}}}}|sf|sf||fS||fS)NT)rr)r)rrrrUrrr2)rH constraintrrSrrrrrr^rargrNr6r7r7r8rsf         z$BigM_Transformation._get_M_from_argsc Cs:|jdk o|ddk}|jdk o*|ddk}|jdtd}d}x|D]} || kr| |}||||||| ||\}}}}|s|s||fS|| krD|} | | }||||||| | |\}}}}|sD|sD||fSqDW|dkr2xN|D]F} d| kr| d}||||||| d|\}}}}|s|s||fSqW||fS)NrT)rr)rrrrUrr) rHrrPrrrrrrrQr^r7r7r8r*sN        z+BigM_Transformation._update_M_from_suffixescCst}|js   5>6 | F6:!r;)RrrtextwrapZpyomo.common.collectionsrrZpyomo.common.configrrZpyomo.common.modelingrZpyomo.common.deprecationrZpyomo.contrib.fbbt.fbbtrZpyomo.contrib.fbbt.intervalr Z pyomo.corer r r r rrrrrrrrrrrrZpyomo.core.base.externalrZpyomo.core.baserrrZpyomo.core.base.componentrrZpyomo.core.base.PyomoModelr r!Zpyomo.core.expr.currentcorercurrentrZ pyomo.gdpr"r#r$Zpyomo.gdp.utilr%r&r'r(r)r*r+r,r-r.Z pyomo.repnr/ functoolsr0sixr1r2weakrefr3r getLoggerrdrUr9registerr;r7r7r7r8 s2    H 0