<commit_before># -*- coding: utf-8 -*-
"""
.. module:: pytfa
   :platform: Unix, Windows
   :synopsis: Thermodynamics-based Flux Analysis

.. moduleauthor:: pyTFA team

Relaxation of models with constraint too tight

"""

from collections import OrderedDict
from copy import deepcopy

from tqdm import tqdm
import pandas as pd
from cobra.util.solver import set_objective
from optlang.exceptions import SolverError

from .constraints import NegativeDeltaG
from .utils import get_solution_value_for_variables, chunk_sum, symbol_sum
from .variables import PosSlackVariable, NegSlackVariable, DeltaGstd, \
    LogConcentration, NegSlackLC, PosSlackLC
from ..utils import numerics

BIGM = numerics.BIGM
BIGM_THERMO = numerics.BIGM_THERMO
BIGM_DG = numerics.BIGM_DG
BIGM_P = numerics.BIGM_P
EPSILON = numerics.EPSILON

def relax_dgo_gurobi(model, relax_obj_type = 0):

    the_cons = [x.constraint._internal_constraint
                for x in model.get_constraints_of_type(NegativeDeltaG)]
    cons_penalities = [1]*len(the_cons)

    # the_vars = [x._internal_variable for x in model.variables]
    # vars_penalities = [1]*len(the_vars)

    grm = model.solver.problem.feasRelax(relaxobjtype=relax_obj_type,
                                         minrelax=True,
                                         constrs=the_cons,
                                         # vars=the_vars,
                                         # lbpen=vars_penalities,
                                         # ubpen=vars_penalities,
                                         vars=None,
                                         lbpen=None,
                                         ubpen=None,
                                         rhspen=cons_penalities)

    return grm

def relax_dgo(tmodel, reactions_to_ignore=(), solver=None, in_place = False):
    """
    :param t_tmodel:
    :type t_tmodel: pytfa.thermo.ThermoModel:
    :param reactions_to_ignore: Iterable of reactions that should not be relaxed
    :param solver: solver to use (e.g. 'optlang-glpk', 'optlang-cplex',
        'optlang-gurobi'
    :return: a cobra_model with relaxed bounds on standard Gibbs free energy
    """

    if solver is None:
        solver = tmodel.solver.interface

    # Create a copy of the cobra_model on which we will perform the slack addition
    slack_model = deepcopy(tmodel)
    slack_model.solver = solver
    slack_model.name = 'SlackModel '+tmodel.name
    slack_model.id = 'SlackModel_'+tmodel.id

    # Ensure the lazy updates are all done
    slack_model.repair()

    if not in_place:
        # Create a copy that will receive the relaxation
        relaxed_model = deepcopy(tmodel)
        relaxed_model.solver = solver
        relaxed_model.name = 'RelaxedModel '+tmodel.name
        relaxed_model.id = 'RelaxedModel_'+tmodel.id
        relaxed_model.repair()
    else:
        relaxed_model = slack_model

    original_objective = relaxed_model.objective

    # Do not relax if cobra_model is already optimal
    # try:
    #     solution = tmodel.optimize()
    # except SolverError as SE:
    #     status = tmodel.solver.status
    #     tmodel.logger.error(SE)
    #     tmodel.logger.warning('Solver status: {}'.format(status))
    # if tmodel.solver.status == OPTIMAL:
    #     raise Exception('Model is already optimal')

    # Find variables that represent standard Gibbs Energy change
    my_dgo = relaxed_model.get_variables_of_type(DeltaGstd)

    # Find constraints that represent negativity of Gibbs Energy change
    my_neg_dg = slack_model.get_constraints_of_type(NegativeDeltaG)

    changes = OrderedDict()
    objective_symbols = []

    slack_model.logger.info('Adding slack constraints')
    hooks = dict()

    slack_model.solver.update()

    for this_neg_dg in tqdm(my_neg_dg, desc='adding slacks'):

        # If there is no thermo, or relaxation forbidden, pass
        if this_neg_dg.id in reactions_to_ignore or this_neg_dg.id not in my_dgo:
            continue

        # Create the negative and positive slack variables
        # We can't queue them because they will be in an expression to declare
        # the constraint
        neg_slack = slack_model.add_variable(NegSlackVariable,
                                             this_neg_dg.reaction, lb=0,
                                             ub=BIGM_DG,
                                             queue=False)
        pos_slack = slack_model.add_variable(PosSlackVariable,
                                             this_neg_dg.reaction, lb=0,
                                             ub=BIGM_DG,
                                             queue=False)

        # Create the new constraint by adding the slack variables to the
        # negative delta G constraint (from the initial cobra_model)
        new_expr = this_neg_dg.constraint.expression
        new_expr += (pos_slack - neg_slack)

        this_reaction = this_neg_dg.reaction
        # # Remove former constraint to override it
        # slack_model.remove_constraint(slack_model._cons_dict[this_neg_dg.name])
        #
        # # Add the new variant
        # slack_model.add_constraint(NegativeDeltaG,
        #                            this_reaction,
        #                            expr=new_expr,
        #                            lb=0,
        #                            ub=0,
        #                            queue=True)

        this_neg_dg.change_expr(new_expr)

        # Update the objective with the new variables
        objective_symbols += [neg_slack,  pos_slack]

    # objective = chunk_sum(objective_symbols)
    objective = symbol_sum(objective_symbols)

    # Change the objective to minimize slack
    set_objective(slack_model, objective)

    # Update variables and constraints references
    slack_model.repair()

    slack_model.logger.info('Optimizing slack model')
    # Relax
    slack_model.objective.direction = 'min'
    relaxation = slack_model.optimize()

    # Extract the relaxation values from the solution, by type
    relaxed_model.logger.info('Extracting relaxation')
    my_neg_slacks = slack_model.get_variables_of_type(NegSlackVariable)
    my_pos_slacks = slack_model.get_variables_of_type(PosSlackVariable)

    neg_slack_values = get_solution_value_for_variables(relaxation,
                                                        my_neg_slacks)
    pos_slack_values = get_solution_value_for_variables(relaxation,
                                                        my_pos_slacks)

    epsilon = relaxed_model.solver.configuration.tolerances.feasibility
    relaxed_model.repair()
    relaxed_model.solver.update()
    # Apply reaction delta G standard bound change
    for this_reaction in tqdm(relaxed_model.reactions, desc = 'applying slack'):
        # No thermo, or relaxation forbidden
        if this_reaction.id in reactions_to_ignore or this_reaction.id not in my_dgo:
            continue

        # Get the standard delta G variable
        the_dgo = my_dgo.get_by_id(this_reaction.id)

        # Get the relaxation
        dgo_delta_lb = \
            neg_slack_values[my_neg_slacks \
                .get_by_id(this_reaction.id).name]
        dgo_delta_ub = \
            pos_slack_values[my_pos_slacks \
                .get_by_id(this_reaction.id).name]

        if in_place:
            the_neg_slack = my_neg_slacks.get_by_id(this_reaction.id)
            the_neg_slack_value = slack_model.solution.raw[the_neg_slack.name]
            the_neg_slack.variable.lb = the_neg_slack_value - epsilon
            the_neg_slack.variable.ub = the_neg_slack_value + epsilon

            the_pos_slack = my_pos_slacks.get_by_id(this_reaction.id)
            the_pos_slack_value = slack_model.solution.raw[the_pos_slack.name]
            the_pos_slack.variable.lb = the_pos_slack_value - epsilon
            the_pos_slack.variable.ub = the_pos_slack_value + epsilon

        # Apply reaction delta G standard bound change
        if dgo_delta_lb > 0 or dgo_delta_ub > 0:

            # Store previous values
            previous_dgo_lb = the_dgo.variable.lb
            previous_dgo_ub = the_dgo.variable.ub

            if not in_place:
                # Apply change
                the_dgo.variable.lb -= (dgo_delta_lb + epsilon)
                the_dgo.variable.ub += (dgo_delta_ub + epsilon)

            # If needed, store that in a report table
            changes[this_reaction.id] = [
                previous_dgo_lb,
                previous_dgo_ub,
                dgo_delta_lb,
                dgo_delta_ub,
                the_dgo.variable.lb,
                the_dgo.variable.ub]

    relaxed_model.repair()
    relaxed_model.logger.info('Testing relaxation')

    relaxed_model.objective = original_objective
    relaxed_model.objective.direction = 'max'

    relaxed_model.optimize()

    if len(changes) == 0:
        # The model is infeasible or something went wrong
        tmodel.logger.error('Relaxation could not complete '
                            '(no DeltaG relaxation found)')
        return relaxed_model, slack_model, None

    # Format relaxation
    relax_table = pd.DataFrame.from_dict(changes,
                                         orient = 'index')
    relax_table.columns = [ 'lb_in',
                            'ub_in',
                            'lb_change',
                            'ub_change',
                            'lb_out',
                            'ub_out']

    return relaxed_model, slack_model, relax_table

def relax_lc(tmodel, metabolites_to_ignore = (), solver = None):
    """

    :param metabolites_to_ignore:
    :param in_tmodel:
    :type in_tmodel: pytfa.thermo.ThermoModel:
    :param min_objective_value:
    :return:
    """

    if solver is None:
        solver = tmodel.solver

    # Create a copy of the cobra_model on which we will perform the slack addition
    slack_model = deepcopy(tmodel)
    slack_model.solver = solver

    # Create a copy that will receive the relaxation
    relaxed_model = deepcopy(tmodel)
    relaxed_model.solver = solver

    # Do not relax if cobra_model is already optimal
    try:
        solution = tmodel.optimize()
    except SolverError as SE:
        status = tmodel.solver.status
        tmodel.logger.error(SE)
        tmodel.logger.warning('Solver status: {}'.format(status))
    # if tmodel.solver.status == OPTIMAL:
    #     raise Exception('Model is already optimal')

    # Find variables that represent standard Gibbs Energy change
    my_lc = relaxed_model.get_variables_of_type(LogConcentration)

    # Find constraints that represent negativity of Gibbs Energy change
    my_neg_dg = slack_model.get_constraints_of_type(NegativeDeltaG)

    changes = OrderedDict()
    objective = 0

    pos_slack = dict()
    neg_slack = dict()

    for this_lc in my_lc:
        if this_lc.name in metabolites_to_ignore:
            continue

        neg_slack[this_lc.name] = slack_model.add_variable(NegSlackLC,
                                             this_lc,
                                             lb= 0,
                                             ub= BIGM_DG)

        pos_slack[this_lc.name] = slack_model.add_variable(PosSlackLC,
                                             this_lc,
                                             lb= 0,
                                             ub= BIGM_DG)

        # Update the objective with the new variables
        objective += (neg_slack[this_lc.name] + pos_slack[this_lc.name])

    for this_neg_dg in my_neg_dg:

        # If there is no thermo, or relaxation forbidden, pass
        if this_neg_dg.id not in my_neg_dg:
            continue

        subs_dict = {k: slack_model.variables.get(k.name) \
                     for k in this_neg_dg.constraint.variables}

        # Create the new constraint by adding the slack variables to the
        # negative delta G constraint (from the initial cobra_model)
        new_expr = this_neg_dg.constraint.expression.subs(subs_dict)

        for this_var in this_neg_dg.constraint.variables:
            if not this_var.name in neg_slack:
                continue

            met_id = pos_slack[this_var.name].id
            the_met = slack_model.metabolites.get_by_id(met_id)
            stoich = this_neg_dg.reaction.metabolites[the_met]
            new_expr += slack_model.RT * stoich \
                        * \
            (pos_slack[this_var.name] - neg_slack[this_var.name])

        # Remove former constraint to override it
        slack_model.remove_constraint(this_neg_dg)

        # Add the new variant
        slack_model.add_constraint(NegativeDeltaG, this_neg_dg.reaction,
                                   expr=new_expr, lb=0, ub=0)

    # Change the objective to minimize slack
    set_objective(slack_model, objective)

    # Update variables and constraints references
    slack_model.repair()

    # Relax
    slack_model.objective.direction = 'min'
    relaxation = slack_model.optimize()

    # Extract the relaxation values from the solution, by type
    my_neg_slacks = slack_model.get_variables_of_type(NegSlackLC)
    my_pos_slacks = slack_model.get_variables_of_type(PosSlackLC)

    neg_slack_values = get_solution_value_for_variables(relaxation,
                                                        my_neg_slacks)
    pos_slack_values = get_solution_value_for_variables(relaxation,
                                                        my_pos_slacks)
    epsilon = relaxed_model.solver.configuration.tolerances.feasibility

    for this_met in relaxed_model.metabolites:
        # No thermo, or relaxation forbidden
        if this_met.id in metabolites_to_ignore or this_met.id not in my_lc:
            continue

        # Get the standard delta G variable
        the_lc = my_lc.get_by_id(this_met.id)

        # Get the relaxation
        lc_delta_lb = neg_slack_values[
            my_neg_slacks \
            .get_by_id(this_met.id).name]
        lc_delta_ub = \
            pos_slack_values[my_pos_slacks \
            .get_by_id(this_met.id).name]

        # Apply reaction delta G standard bound change
        if lc_delta_lb > 0 or lc_delta_ub > 0:

            # Store previous values
            previous_lc_lb = the_lc.variable.lb
            previous_lc_ub = the_lc.variable.ub

            # Apply change
            the_lc.variable.lb -= (lc_delta_lb + epsilon)
            the_lc.variable.ub += (lc_delta_ub + epsilon)

            # If needed, store that in a report table
            changes[this_met.id] = [
                previous_lc_lb,
                previous_lc_ub,
                lc_delta_lb,
                lc_delta_ub,
                the_lc.variable.lb,
                the_lc.variable.ub]

    # Obtain relaxation
    relaxed_model.optimize()

    # Format relaxation
    relax_table = pd.DataFrame.from_dict(changes,
                                         orient = 'index')
    relax_table.columns = [ 'lb_in',
                            'ub_in',
                            'lb_change',
                            'ub_change',
                            'lb_out',
                            'ub_out']

    tmodel.logger.info('\n' + relax_table.__str__())

    relaxed_model.relaxation = relax_table

    return relaxed_model, slack_model, relax_table
<commit_msg>ENH: Improved solver parameters (Gurobi) for relaxation<commit_after># -*- coding: utf-8 -*-
"""
.. module:: pytfa
   :platform: Unix, Windows
   :synopsis: Thermodynamics-based Flux Analysis

.. moduleauthor:: pyTFA team

Relaxation of models with constraint too tight

"""

from collections import OrderedDict
from copy import deepcopy

from tqdm import tqdm
import pandas as pd
from cobra.util.solver import set_objective
from optlang.exceptions import SolverError

from .constraints import NegativeDeltaG
from .config import dg_relax_config
from .utils import get_solution_value_for_variables, chunk_sum, symbol_sum
from .variables import PosSlackVariable, NegSlackVariable, DeltaGstd, \
    LogConcentration, NegSlackLC, PosSlackLC
from ..utils import numerics

BIGM = numerics.BIGM
BIGM_THERMO = numerics.BIGM_THERMO
BIGM_DG = numerics.BIGM_DG
BIGM_P = numerics.BIGM_P
EPSILON = numerics.EPSILON

def relax_dgo_gurobi(model, relax_obj_type = 0):

    the_cons = [x.constraint._internal_constraint
                for x in model.get_constraints_of_type(NegativeDeltaG)]
    cons_penalities = [1]*len(the_cons)

    # the_vars = [x._internal_variable for x in model.variables]
    # vars_penalities = [1]*len(the_vars)

    grm = model.solver.problem.feasRelax(relaxobjtype=relax_obj_type,
                                         minrelax=True,
                                         constrs=the_cons,
                                         # vars=the_vars,
                                         # lbpen=vars_penalities,
                                         # ubpen=vars_penalities,
                                         vars=None,
                                         lbpen=None,
                                         ubpen=None,
                                         rhspen=cons_penalities)

    return grm

def relax_dgo(tmodel, reactions_to_ignore=(), solver=None, in_place = False):
    """
    :param t_tmodel:
    :type t_tmodel: pytfa.thermo.ThermoModel:
    :param reactions_to_ignore: Iterable of reactions that should not be relaxed
    :param solver: solver to use (e.g. 'optlang-glpk', 'optlang-cplex',
        'optlang-gurobi'
    :return: a cobra_model with relaxed bounds on standard Gibbs free energy
    """

    if solver is None:
        solver = tmodel.solver.interface

    # Create a copy of the cobra_model on which we will perform the slack addition
    slack_model = deepcopy(tmodel)
    slack_model.solver = solver
    slack_model.name = 'SlackModel '+tmodel.name
    slack_model.id = 'SlackModel_'+tmodel.id

    # Ensure the lazy updates are all done
    slack_model.repair()

    if not in_place:
        # Create a copy that will receive the relaxation
        relaxed_model = deepcopy(tmodel)
        relaxed_model.solver = solver
        relaxed_model.name = 'RelaxedModel '+tmodel.name
        relaxed_model.id = 'RelaxedModel_'+tmodel.id
        relaxed_model.repair()
    else:
        relaxed_model = slack_model

    dg_relax_config(slack_model)

    original_objective = relaxed_model.objective

    # Do not relax if cobra_model is already optimal
    # try:
    #     solution = tmodel.optimize()
    # except SolverError as SE:
    #     status = tmodel.solver.status
    #     tmodel.logger.error(SE)
    #     tmodel.logger.warning('Solver status: {}'.format(status))
    # if tmodel.solver.status == OPTIMAL:
    #     raise Exception('Model is already optimal')

    # Find variables that represent standard Gibbs Energy change
    my_dgo = relaxed_model.get_variables_of_type(DeltaGstd)

    # Find constraints that represent negativity of Gibbs Energy change
    my_neg_dg = slack_model.get_constraints_of_type(NegativeDeltaG)

    changes = OrderedDict()
    objective_symbols = []

    slack_model.logger.info('Adding slack constraints')

    slack_model.solver.update()

    for this_neg_dg in tqdm(my_neg_dg, desc='adding slacks'):

        # If there is no thermo, or relaxation forbidden, pass
        if this_neg_dg.id in reactions_to_ignore or this_neg_dg.id not in my_dgo:
            continue

        # Create the negative and positive slack variables
        # We can't queue them because they will be in an expression to declare
        # the constraint
        neg_slack = slack_model.add_variable(NegSlackVariable,
                                             this_neg_dg.reaction, lb=0,
                                             ub=BIGM_DG,
                                             queue=False)
        pos_slack = slack_model.add_variable(PosSlackVariable,
                                             this_neg_dg.reaction, lb=0,
                                             ub=BIGM_DG,
                                             queue=False)

        # Create the new constraint by adding the slack variables to the
        # negative delta G constraint (from the initial cobra_model)
        new_expr = this_neg_dg.constraint.expression
        new_expr += (pos_slack - neg_slack)

        this_reaction = this_neg_dg.reaction
        # # Remove former constraint to override it
        # slack_model.remove_constraint(slack_model._cons_dict[this_neg_dg.name])
        #
        # # Add the new variant
        # slack_model.add_constraint(NegativeDeltaG,
        #                            this_reaction,
        #                            expr=new_expr,
        #                            lb=0,
        #                            ub=0,
        #                            queue=True)

        this_neg_dg.change_expr(new_expr)

        # Update the objective with the new variables
        objective_symbols += [neg_slack,  pos_slack]

    # objective = chunk_sum(objective_symbols)
    objective = symbol_sum(objective_symbols)

    # Change the objective to minimize slack
    set_objective(slack_model, objective)

    # Update variables and constraints references
    slack_model.repair()

    slack_model.logger.info('Optimizing slack model')
    # Relax
    slack_model.objective.direction = 'min'
    relaxation = slack_model.optimize()

    # Extract the relaxation values from the solution, by type
    relaxed_model.logger.info('Extracting relaxation')
    my_neg_slacks = slack_model.get_variables_of_type(NegSlackVariable)
    my_pos_slacks = slack_model.get_variables_of_type(PosSlackVariable)

    neg_slack_values = get_solution_value_for_variables(relaxation,
                                                        my_neg_slacks)
    pos_slack_values = get_solution_value_for_variables(relaxation,
                                                        my_pos_slacks)

    epsilon = relaxed_model.solver.configuration.tolerances.feasibility
    relaxed_model.repair()
    relaxed_model.solver.update()
    # Apply reaction delta G standard bound change
    for this_reaction in tqdm(relaxed_model.reactions, desc = 'applying slack'):
        # No thermo, or relaxation forbidden
        if this_reaction.id in reactions_to_ignore or this_reaction.id not in my_dgo:
            continue

        # Get the standard delta G variable
        the_dgo = my_dgo.get_by_id(this_reaction.id)

        # Get the relaxation
        dgo_delta_lb = \
            neg_slack_values[my_neg_slacks \
                .get_by_id(this_reaction.id).name]
        dgo_delta_ub = \
            pos_slack_values[my_pos_slacks \
                .get_by_id(this_reaction.id).name]

        if in_place:
            the_neg_slack = my_neg_slacks.get_by_id(this_reaction.id)
            the_neg_slack_value = slack_model.solution.raw[the_neg_slack.name]
            the_neg_slack.variable.lb = the_neg_slack_value - epsilon
            the_neg_slack.variable.ub = the_neg_slack_value + epsilon

            the_pos_slack = my_pos_slacks.get_by_id(this_reaction.id)
            the_pos_slack_value = slack_model.solution.raw[the_pos_slack.name]
            the_pos_slack.variable.lb = the_pos_slack_value - epsilon
            the_pos_slack.variable.ub = the_pos_slack_value + epsilon

        # Apply reaction delta G standard bound change
        if dgo_delta_lb > 0 or dgo_delta_ub > 0:

            # Store previous values
            previous_dgo_lb = the_dgo.variable.lb
            previous_dgo_ub = the_dgo.variable.ub

            if not in_place:
                # Apply change
                the_dgo.variable.lb -= (dgo_delta_lb + epsilon)
                the_dgo.variable.ub += (dgo_delta_ub + epsilon)

            # If needed, store that in a report table
            changes[this_reaction.id] = [
                previous_dgo_lb,
                previous_dgo_ub,
                dgo_delta_lb,
                dgo_delta_ub,
                the_dgo.variable.lb,
                the_dgo.variable.ub]

    relaxed_model.repair()
    relaxed_model.logger.info('Testing relaxation')

    relaxed_model.objective = original_objective
    relaxed_model.objective.direction = 'max'

    relaxed_model.optimize()

    if len(changes) == 0:
        # The model is infeasible or something went wrong
        tmodel.logger.error('Relaxation could not complete '
                            '(no DeltaG relaxation found)')
        return relaxed_model, slack_model, None

    # Format relaxation
    relax_table = pd.DataFrame.from_dict(changes,
                                         orient = 'index')
    relax_table.columns = [ 'lb_in',
                            'ub_in',
                            'lb_change',
                            'ub_change',
                            'lb_out',
                            'ub_out']

    return relaxed_model, slack_model, relax_table

def relax_lc(tmodel, metabolites_to_ignore = (), solver = None):
    """

    :param metabolites_to_ignore:
    :param in_tmodel:
    :type in_tmodel: pytfa.thermo.ThermoModel:
    :param min_objective_value:
    :return:
    """

    if solver is None:
        solver = tmodel.solver

    # Create a copy of the cobra_model on which we will perform the slack addition
    slack_model = deepcopy(tmodel)
    slack_model.solver = solver

    # Create a copy that will receive the relaxation
    relaxed_model = deepcopy(tmodel)
    relaxed_model.solver = solver

    # Do not relax if cobra_model is already optimal
    try:
        solution = tmodel.optimize()
    except SolverError as SE:
        status = tmodel.solver.status
        tmodel.logger.error(SE)
        tmodel.logger.warning('Solver status: {}'.format(status))
    # if tmodel.solver.status == OPTIMAL:
    #     raise Exception('Model is already optimal')

    # Find variables that represent standard Gibbs Energy change
    my_lc = relaxed_model.get_variables_of_type(LogConcentration)

    # Find constraints that represent negativity of Gibbs Energy change
    my_neg_dg = slack_model.get_constraints_of_type(NegativeDeltaG)

    changes = OrderedDict()
    objective = 0

    pos_slack = dict()
    neg_slack = dict()

    for this_lc in my_lc:
        if this_lc.name in metabolites_to_ignore:
            continue

        neg_slack[this_lc.name] = slack_model.add_variable(NegSlackLC,
                                             this_lc,
                                             lb= 0,
                                             ub= BIGM_DG)

        pos_slack[this_lc.name] = slack_model.add_variable(PosSlackLC,
                                             this_lc,
                                             lb= 0,
                                             ub= BIGM_DG)

        # Update the objective with the new variables
        objective += (neg_slack[this_lc.name] + pos_slack[this_lc.name])

    for this_neg_dg in my_neg_dg:

        # If there is no thermo, or relaxation forbidden, pass
        if this_neg_dg.id not in my_neg_dg:
            continue

        subs_dict = {k: slack_model.variables.get(k.name) \
                     for k in this_neg_dg.constraint.variables}

        # Create the new constraint by adding the slack variables to the
        # negative delta G constraint (from the initial cobra_model)
        new_expr = this_neg_dg.constraint.expression.subs(subs_dict)

        for this_var in this_neg_dg.constraint.variables:
            if not this_var.name in neg_slack:
                continue

            met_id = pos_slack[this_var.name].id
            the_met = slack_model.metabolites.get_by_id(met_id)
            stoich = this_neg_dg.reaction.metabolites[the_met]
            new_expr += slack_model.RT * stoich \
                        * \
            (pos_slack[this_var.name] - neg_slack[this_var.name])

        # Remove former constraint to override it
        slack_model.remove_constraint(this_neg_dg)

        # Add the new variant
        slack_model.add_constraint(NegativeDeltaG, this_neg_dg.reaction,
                                   expr=new_expr, lb=0, ub=0)

    # Change the objective to minimize slack
    set_objective(slack_model, objective)

    # Update variables and constraints references
    slack_model.repair()

    # Relax
    slack_model.objective.direction = 'min'
    relaxation = slack_model.optimize()

    # Extract the relaxation values from the solution, by type
    my_neg_slacks = slack_model.get_variables_of_type(NegSlackLC)
    my_pos_slacks = slack_model.get_variables_of_type(PosSlackLC)

    neg_slack_values = get_solution_value_for_variables(relaxation,
                                                        my_neg_slacks)
    pos_slack_values = get_solution_value_for_variables(relaxation,
                                                        my_pos_slacks)
    epsilon = relaxed_model.solver.configuration.tolerances.feasibility

    for this_met in relaxed_model.metabolites:
        # No thermo, or relaxation forbidden
        if this_met.id in metabolites_to_ignore or this_met.id not in my_lc:
            continue

        # Get the standard delta G variable
        the_lc = my_lc.get_by_id(this_met.id)

        # Get the relaxation
        lc_delta_lb = neg_slack_values[
            my_neg_slacks \
            .get_by_id(this_met.id).name]
        lc_delta_ub = \
            pos_slack_values[my_pos_slacks \
            .get_by_id(this_met.id).name]

        # Apply reaction delta G standard bound change
        if lc_delta_lb > 0 or lc_delta_ub > 0:

            # Store previous values
            previous_lc_lb = the_lc.variable.lb
            previous_lc_ub = the_lc.variable.ub

            # Apply change
            the_lc.variable.lb -= (lc_delta_lb + epsilon)
            the_lc.variable.ub += (lc_delta_ub + epsilon)

            # If needed, store that in a report table
            changes[this_met.id] = [
                previous_lc_lb,
                previous_lc_ub,
                lc_delta_lb,
                lc_delta_ub,
                the_lc.variable.lb,
                the_lc.variable.ub]

    # Obtain relaxation
    relaxed_model.optimize()

    # Format relaxation
    relax_table = pd.DataFrame.from_dict(changes,
                                         orient = 'index')
    relax_table.columns = [ 'lb_in',
                            'ub_in',
                            'lb_change',
                            'ub_change',
                            'lb_out',
                            'ub_out']

    tmodel.logger.info('\n' + relax_table.__str__())

    relaxed_model.relaxation = relax_table

    return relaxed_model, slack_model, relax_table
<|endoftext|>


<commit_before>#!/usr/bin/env python
# coding=utf-8

"""
Problem Definition :

Let d(n) be defined as the sum of proper divisors of n (numbers less than n which divide evenly into n).
If d(a) = b and d(b) = a, where a ≠ b, then a and b are an amicable pair and each of a and b are called amicable numbers.

For example, the proper divisors of 220 are 1, 2, 4, 5, 10, 11, 20, 22, 44, 55 and 110; therefore d(220) = 284. The proper divisors of 284 are 1, 2, 4, 71 and 142; so d(284) = 220.

Evaluate the sum of all the amicable numbers under 10000.

"""

import math

def find_sum_factors(num):
    number = 2
    addition = 1

    while number < int(math.sqrt(num)):
        if num % number == 0:
            addition += number + (num/number)
        number += 1
    if math.sqrt(num) == number:
            addition += number
    return addition

def main():
    sum_div = [0]*10001
    amicable = list()

    for i in xrange(1, 10001):
        sum_div[i] = find_sum_factors(i)

    for x in xrange(1, 10001):
        if sum_div[x] < 10001:
            if sum_div[x] != 1 and sum_div[x] != x and x == sum_div[sum_div[x]]:
                amicable.append(x)

    print sum(amicable)

if __name__ == '__main__':
    main()<commit_msg>logic updated<commit_after>#!/usr/bin/env python
# coding=utf-8

"""
Problem Definition :

Let d(n) be defined as the sum of proper divisors of n (numbers less than n which divide evenly into n).
If d(a) = b and d(b) = a, where a ≠ b, then a and b are an amicable pair and each of a and b are called amicable numbers.

For example, the proper divisors of 220 are 1, 2, 4, 5, 10, 11, 20, 22, 44, 55 and 110; therefore d(220) = 284. The proper divisors of 284 are 1, 2, 4, 71 and 142; so d(284) = 220.

Evaluate the sum of all the amicable numbers under 10000.

"""

import math

def find_sum_factors(num):
    number = 2
    addition = 1

    while number < int(math.sqrt(num)):
        if num % number == 0:
            addition += number + int((num/number))
        number += 1

    if math.sqrt(num) == number:
            addition += number

    return int(addition)

def main():
    limit = 10001
    sum_factors_dict = [0 for i in range(limit)]
    amicable = list()

    for i in range(1, limit):
        sum_factors_dict[i] = find_sum_factors(i)

    for x in range(2, limit):
        if -1 < sum_factors_dict[x] < limit and sum_factors_dict[x] != x and x == sum_factors_dict[sum_factors_dict[x]]:
            amicable.append(x)
            amicable.append(sum_factors_dict[x])
            sum_factors_dict[sum_factors_dict[x]] = -1
    print(sum(amicable))

if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before>#!/usr/bin/env python
# coding=utf-8

# IN PROGRESS #
"""
Problem Definition :

Pentagonal numbers are generated by the formula, Pn=n(3n-1)/2. The first ten pentagonal numbers are:

1, 5, 12, 22, 35, 51, 70, 92, 117, 145, ...

It can be seen that P4 + P7 = 22 + 70 = 92 = P8. However, their difference, 70 - 22 = 48, is not pentagonal.

Find the pair of pentagonal numbers Pj and Pk for which their sum and difference are pentagonal and D = Pk − Pj is minimised what is the value of D?

"""

__author__ = 'vivek'

import time
import math

startTime = time.clock()

def pentagonal(n):
    return n*(3*n - 1)/2

penta_numbers = [pentagonal(x) for x in xrange(1,5000)]

print "Run time...{} secs \n".format(round(time.clock() - startTime, 4))
answers = []

print(penta_numbers[1998])
print(penta_numbers[4998])

for i in xrange(0,1999):
    for j in xrange(i+1,1999):
        number1 = penta_numbers[i]
        number2 = penta_numbers[j]
        if number2+number1 in penta_numbers:# and number2-number1 in penta_numbers:
            answers.append(number1+ number2)
            #print("Found")
            print(number1, number2)
if answers:
    print(min(answers))
    print(len(answers))
else:
    print("Not found")

#print(penta_numbers)

print "Run time...{} secs \n".format(round(time.clock() - startTime, 4))
<commit_msg>Pantagone problem solved.<commit_after>#!/usr/bin/env python
# coding=utf-8

"""
Problem Definition :

Pentagonal numbers are generated by the formula, Pn=n(3n-1)/2. The first ten pentagonal numbers are:

1, 5, 12, 22, 35, 51, 70, 92, 117, 145, ...

It can be seen that P4 + P7 = 22 + 70 = 92 = P8. However, their difference, 70 - 22 = 48, is not pentagonal.

Find the pair of pentagonal numbers Pj and Pk for which their sum and difference are pentagonal and D = Pk − Pj is minimised what is the value of D?

"""

__author__ = 'vivek'

import time
import math

def pentagonal(n):
    return n*(3*n - 1)/2

def main():

    start_time = time.clock()
    limit = 5000
    penta_numbers = [pentagonal(x) for x in range(1, limit)]
    found = 0
    for i in range(0, limit-1):
        for j in xrange(0, i):
            number1 = penta_numbers[i]
            number2 = penta_numbers[j]
            if number1 - number2 in penta_numbers:# and number2-number1 in penta_numbers:
                if number1 + number2 in penta_numbers:
                    print(number1, number2, number1-number2)
                    found = 1
                    break
        if found:
            break
    print("Run time...{} secs \n".format(round(time.clock() - start_time, 4)))

if __name__ == "__main__":
    main()
<|endoftext|>


<commit_before>##########################################################################
#
#  Copyright (c) 2011, John Haddon. All rights reserved.
#  Copyright (c) 2011, Image Engine Design Inc. All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#
#      * Redistributions of source code must retain the above
#        copyright notice, this list of conditions and the following
#        disclaimer.
#
#      * Redistributions in binary form must reproduce the above
#        copyright notice, this list of conditions and the following
#        disclaimer in the documentation and/or other materials provided with
#        the distribution.
#
#      * Neither the name of John Haddon nor the names of
#        any other contributors to this software may be used to endorse or
#        promote products derived from this software without specific prior
#        written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
#  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
#  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
#  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
##########################################################################

from __future__ import with_statement

import IECore

import Gaffer
import GafferUI

QtGui = GafferUI._qtImport( "QtGui" )

## This class forms the base class for all uis for nodes. It provides simple methods for building a ui
# structured using tabs and collapsible elements, and allows customisation of the widget types used for
# each Plug.
class NodeUI( GafferUI.Widget ) :

	def __init__( self, node ) :

		self.__currentColumn = GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Vertical, spacing=2 )

		GafferUI.Widget.__init__( self, self.__currentColumn )

		self.__node = node

		self._build()

	## Returns the node the ui is being created for.
	def _node( self ) :

		return self.__node

	def _tab( self, label ) :

		raise NotImplementedError

	def _scrollable( self ) :

		class ScrollableContext() :

			def __init__( self, nodeUI ) :

				self.__nodeUI = nodeUI

			def __enter__( self ) :

				sc = GafferUI.ScrolledContainer(
					horizontalMode = GafferUI.ScrolledContainer.ScrollMode.Never,
					verticalMode = GafferUI.ScrolledContainer.ScrollMode.Automatic,
					borderWidth = 8
				)

				co = GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Vertical, spacing=2 )
				sc.setChild( co )

				self.__prevColumn = self.__nodeUI._NodeUI__currentColumn

				self.__nodeUI._NodeUI__currentColumn.append( sc )
				self.__nodeUI._NodeUI__currentColumn = co

			def __exit__( self, type, value, traceBack ) :

				self.__nodeUI._NodeUI__currentColumn = self.__prevColumn

		return ScrollableContext( self )

	def _collapsible( self, label ) :

		class CollapsibleContext() :

			def __init__( self, nodeUI ) :

				self.__nodeUI = nodeUI
				self.__label = label

			def __enter__( self ) :

				cl = GafferUI.Collapsible( self.__label, collapsed=True )
				co = GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Vertical, spacing=2 )
				cl.setChild( co )

				self.__prevColumn = self.__nodeUI._NodeUI__currentColumn

				self.__nodeUI._NodeUI__currentColumn.append( cl )
				self.__nodeUI._NodeUI__currentColumn = co

			def __exit__( self, type, value, traceBack ) :

				self.__nodeUI._NodeUI__currentColumn = self.__prevColumn

		return CollapsibleContext( self )

	def _addWidget( self, widget ) :

		self.__currentColumn.append( widget )

	def _addPlugWidget( self, plugOrPlugPath ) :

		if isinstance( plugOrPlugPath, basestring ) :
			plug = self._node().getChild( plugOrPlugPath )
		else :
			plug = plugOrPlugPath

		vw = GafferUI.PlugValueWidget.create( plug )
		if vw :
			w = GafferUI.PlugWidget( vw )
			self._addWidget( w )

	## This method is called from the constructor to build the ui. It is
	# intended to be overriden in derived classes.
	def _build( self ) :

		with self._scrollable() :
			self.__buildWalk( self._node() )

	def __buildWalk( self, parent ) :

		plugs = [ x for x in parent.children() if x.isInstanceOf( Gaffer.Plug.staticTypeId() ) ]
		plugs = [ x for x in plugs if x.direction()==Gaffer.Plug.Direction.In and not x.getName().startswith( "__" ) ]
		for plug in plugs :

			if plug.typeId()==Gaffer.CompoundPlug.staticTypeId() :

				with self._collapsible( IECore.CamelCase.toSpaced( plug.getName() ) ) :
					self.__buildWalk( plug )

			else :

				self._addPlugWidget( plug )

	@staticmethod
	def _registerPlugWidget( self, nodeTypeId, plugPath, widgetType, **kw ) :

		raise NotImplementedError

	## Creates a NodeUI instance for the specified node.
	@classmethod
	def create( cls, node ) :

		nodeHierarchy = IECore.RunTimeTyped.baseTypeIds( node.typeId() )
		for typeId in [ node.typeId() ] + nodeHierarchy :
			nodeUI = cls.__nodeUIs.get( typeId, None )
			if nodeUI is not None :
				return nodeUI( node )

		assert( 0 )

	__nodeUIs = {}
	## Registers a subclass of NodeUI to be used with a specific node type.
	@classmethod
	def registerNodeUI( cls, nodeTypeId, nodeUIType ) :

		assert( issubclass( nodeUIType, NodeUI ) )

		cls.__nodeUIs[nodeTypeId] = nodeUIType

NodeUI.registerNodeUI( Gaffer.Node.staticTypeId(), NodeUI )
<commit_msg>Collapsible method can pass arbitrary arguments to the Collapsible constructor.<commit_after>##########################################################################
#
#  Copyright (c) 2011, John Haddon. All rights reserved.
#  Copyright (c) 2011, Image Engine Design Inc. All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#
#      * Redistributions of source code must retain the above
#        copyright notice, this list of conditions and the following
#        disclaimer.
#
#      * Redistributions in binary form must reproduce the above
#        copyright notice, this list of conditions and the following
#        disclaimer in the documentation and/or other materials provided with
#        the distribution.
#
#      * Neither the name of John Haddon nor the names of
#        any other contributors to this software may be used to endorse or
#        promote products derived from this software without specific prior
#        written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
#  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
#  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
#  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
##########################################################################

from __future__ import with_statement

import IECore

import Gaffer
import GafferUI

QtGui = GafferUI._qtImport( "QtGui" )

## This class forms the base class for all uis for nodes. It provides simple methods for building a ui
# structured using tabs and collapsible elements, and allows customisation of the widget types used for
# each Plug.
class NodeUI( GafferUI.Widget ) :

	def __init__( self, node ) :

		self.__currentColumn = GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Vertical, spacing=2 )

		GafferUI.Widget.__init__( self, self.__currentColumn )

		self.__node = node

		self._build()

	## Returns the node the ui is being created for.
	def _node( self ) :

		return self.__node

	def _tab( self, label ) :

		raise NotImplementedError

	def _scrollable( self ) :

		class ScrollableContext() :

			def __init__( self, nodeUI ) :

				self.__nodeUI = nodeUI

			def __enter__( self ) :

				sc = GafferUI.ScrolledContainer(
					horizontalMode = GafferUI.ScrolledContainer.ScrollMode.Never,
					verticalMode = GafferUI.ScrolledContainer.ScrollMode.Automatic,
					borderWidth = 8
				)

				co = GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Vertical, spacing=2 )
				sc.setChild( co )

				self.__prevColumn = self.__nodeUI._NodeUI__currentColumn

				self.__nodeUI._NodeUI__currentColumn.append( sc )
				self.__nodeUI._NodeUI__currentColumn = co

			def __exit__( self, type, value, traceBack ) :

				self.__nodeUI._NodeUI__currentColumn = self.__prevColumn

		return ScrollableContext( self )

	def _collapsible( self, **kw ) :

		class CollapsibleContext() :

			def __init__( self, nodeUI, collapsibleKeywords ) :

				self.__nodeUI = nodeUI
				self.__collapsibleKeywords = collapsibleKeywords

			def __enter__( self ) :

				cl = GafferUI.Collapsible( **self.__collapsibleKeywords )
				co = GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Vertical, spacing=2 )
				cl.setChild( co )

				self.__prevColumn = self.__nodeUI._NodeUI__currentColumn

				self.__nodeUI._NodeUI__currentColumn.append( cl )
				self.__nodeUI._NodeUI__currentColumn = co

			def __exit__( self, type, value, traceBack ) :

				self.__nodeUI._NodeUI__currentColumn = self.__prevColumn

		return CollapsibleContext( self, kw )

	def _addWidget( self, widget ) :

		self.__currentColumn.append( widget )

	def _addPlugWidget( self, plugOrPlugPath ) :

		if isinstance( plugOrPlugPath, basestring ) :
			plug = self._node().getChild( plugOrPlugPath )
		else :
			plug = plugOrPlugPath

		vw = GafferUI.PlugValueWidget.create( plug )
		if vw :
			w = GafferUI.PlugWidget( vw )
			self._addWidget( w )

	## This method is called from the constructor to build the ui. It is
	# intended to be overriden in derived classes.
	def _build( self ) :

		with self._scrollable() :
			self.__buildWalk( self._node() )

	def __buildWalk( self, parent ) :

		plugs = [ x for x in parent.children() if x.isInstanceOf( Gaffer.Plug.staticTypeId() ) ]
		plugs = [ x for x in plugs if x.direction()==Gaffer.Plug.Direction.In and not x.getName().startswith( "__" ) ]
		for plug in plugs :

			if plug.typeId()==Gaffer.CompoundPlug.staticTypeId() :

				with self._collapsible( label = IECore.CamelCase.toSpaced( plug.getName() ), collapsed=True ) :
					self.__buildWalk( plug )

			else :

				self._addPlugWidget( plug )

	@staticmethod
	def _registerPlugWidget( self, nodeTypeId, plugPath, widgetType, **kw ) :

		raise NotImplementedError

	## Creates a NodeUI instance for the specified node.
	@classmethod
	def create( cls, node ) :

		nodeHierarchy = IECore.RunTimeTyped.baseTypeIds( node.typeId() )
		for typeId in [ node.typeId() ] + nodeHierarchy :
			nodeUI = cls.__nodeUIs.get( typeId, None )
			if nodeUI is not None :
				return nodeUI( node )

		assert( 0 )

	__nodeUIs = {}
	## Registers a subclass of NodeUI to be used with a specific node type.
	@classmethod
	def registerNodeUI( cls, nodeTypeId, nodeUIType ) :

		assert( issubclass( nodeUIType, NodeUI ) )

		cls.__nodeUIs[nodeTypeId] = nodeUIType

NodeUI.registerNodeUI( Gaffer.Node.staticTypeId(), NodeUI )
<|endoftext|>


<commit_before>##########################################################################
#
#  Copyright (c) 2011, John Haddon. All rights reserved.
#  Copyright (c) 2011, Image Engine Design Inc. All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#
#      * Redistributions of source code must retain the above
#        copyright notice, this list of conditions and the following
#        disclaimer.
#
#      * Redistributions in binary form must reproduce the above
#        copyright notice, this list of conditions and the following
#        disclaimer in the documentation and/or other materials provided with
#        the distribution.
#
#      * Neither the name of John Haddon nor the names of
#        any other contributors to this software may be used to endorse or
#        promote products derived from this software without specific prior
#        written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
#  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
#  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
#  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
##########################################################################

from __future__ import with_statement

import IECore

import Gaffer
import GafferUI

## This class forms the base class for all uis for nodes. It provides simple methods for building a ui
# structured using tabs and collapsible elements, and allows customisation of the widget types used for
# each Plug.
class NodeUI( GafferUI.Widget ) :

	def __init__( self, node ) :

		self.__currentColumn = GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Vertical, spacing=2 )

		GafferUI.Widget.__init__( self, self.__currentColumn )

		self.__node = node

		self._build()

	## Returns the node the ui is being created for.
	def _node( self ) :

		return self.__node

	def _tab( self, label ) :

		raise NotImplementedError

	def _scrollable( self ) :

		class ScrollableContext() :

			def __init__( self, nodeUI ) :

				self.__nodeUI = nodeUI

			def __enter__( self ) :

				sc = GafferUI.ScrolledContainer(
					horizontalMode = GafferUI.ScrolledContainer.ScrollMode.Never,
					verticalMode = GafferUI.ScrolledContainer.ScrollMode.Automatic,
					borderWidth = 8
				)

				co = GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Vertical, spacing=2 )
				sc.setChild( co )

				self.__prevColumn = self.__nodeUI._NodeUI__currentColumn

				self.__nodeUI._NodeUI__currentColumn.append( sc )
				self.__nodeUI._NodeUI__currentColumn = co

			def __exit__( self, type, value, traceBack ) :

				self.__nodeUI._NodeUI__currentColumn = self.__prevColumn

		return ScrollableContext( self )

	def _collapsible( self, **kw ) :

		class CollapsibleContext() :

			def __init__( self, nodeUI, collapsibleKeywords ) :

				self.__nodeUI = nodeUI
				self.__collapsibleKeywords = collapsibleKeywords

			def __enter__( self ) :

				cl = GafferUI.Collapsible( **self.__collapsibleKeywords )
				co = GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Vertical, spacing=2 )
				cl.setChild( co )

				self.__prevColumn = self.__nodeUI._NodeUI__currentColumn

				self.__nodeUI._NodeUI__currentColumn.append( cl )
				self.__nodeUI._NodeUI__currentColumn = co

			def __exit__( self, type, value, traceBack ) :

				self.__nodeUI._NodeUI__currentColumn = self.__prevColumn

		return CollapsibleContext( self, kw )

	def _addWidget( self, widget ) :

		self.__currentColumn.append( widget )

	def _addPlugWidget( self, plugOrPlugPath ) :

		if isinstance( plugOrPlugPath, basestring ) :
			plug = self._node().getChild( plugOrPlugPath )
		else :
			plug = plugOrPlugPath

		vw = GafferUI.PlugValueWidget.create( plug )
		if vw :
			w = GafferUI.PlugWidget( vw )
			self._addWidget( w )

	## This method is called from the constructor to build the ui. It is
	# intended to be overriden in derived classes.
	def _build( self ) :

		with self._scrollable() :
			self.__buildWalk( self._node() )

	def __buildWalk( self, parent ) :

		plugs = [ x for x in parent.children() if x.isInstanceOf( Gaffer.Plug.staticTypeId() ) ]
		plugs = [ x for x in plugs if x.direction()==Gaffer.Plug.Direction.In and not x.getName().startswith( "__" ) ]
		for plug in plugs :

			if plug.typeId()==Gaffer.CompoundPlug.staticTypeId() :

				with self._collapsible( label = IECore.CamelCase.toSpaced( plug.getName() ), collapsed=True ) :
					self.__buildWalk( plug )

			else :

				self._addPlugWidget( plug )

	@staticmethod
	def _registerPlugWidget( self, nodeTypeId, plugPath, widgetType, **kw ) :

		raise NotImplementedError

	## Creates a NodeUI instance for the specified node.
	@classmethod
	def create( cls, node ) :

		nodeHierarchy = IECore.RunTimeTyped.baseTypeIds( node.typeId() )
		for typeId in [ node.typeId() ] + nodeHierarchy :
			nodeUI = cls.__nodeUIs.get( typeId, None )
			if nodeUI is not None :
				return nodeUI( node )

		assert( 0 )

	__nodeUIs = {}
	## Registers a subclass of NodeUI to be used with a specific node type.
	@classmethod
	def registerNodeUI( cls, nodeTypeId, nodeUIType ) :

		assert( issubclass( nodeUIType, NodeUI ) )

		cls.__nodeUIs[nodeTypeId] = nodeUIType

NodeUI.registerNodeUI( Gaffer.Node.staticTypeId(), NodeUI )
<commit_msg>Slightly more generous column spacing.<commit_after>##########################################################################
#
#  Copyright (c) 2011, John Haddon. All rights reserved.
#  Copyright (c) 2011, Image Engine Design Inc. All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#
#      * Redistributions of source code must retain the above
#        copyright notice, this list of conditions and the following
#        disclaimer.
#
#      * Redistributions in binary form must reproduce the above
#        copyright notice, this list of conditions and the following
#        disclaimer in the documentation and/or other materials provided with
#        the distribution.
#
#      * Neither the name of John Haddon nor the names of
#        any other contributors to this software may be used to endorse or
#        promote products derived from this software without specific prior
#        written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
#  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
#  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
#  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
##########################################################################

from __future__ import with_statement

import IECore

import Gaffer
import GafferUI

## This class forms the base class for all uis for nodes. It provides simple methods for building a ui
# structured using tabs and collapsible elements, and allows customisation of the widget types used for
# each Plug.
class NodeUI( GafferUI.Widget ) :

	_columnSpacing = 4

	def __init__( self, node ) :

		self.__currentColumn = GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Vertical, spacing=self._columnSpacing )

		GafferUI.Widget.__init__( self, self.__currentColumn )

		self.__node = node

		self._build()

	## Returns the node the ui is being created for.
	def _node( self ) :

		return self.__node

	def _tab( self, label ) :

		raise NotImplementedError

	def _scrollable( self ) :

		class ScrollableContext() :

			def __init__( self, nodeUI ) :

				self.__nodeUI = nodeUI

			def __enter__( self ) :

				sc = GafferUI.ScrolledContainer(
					horizontalMode = GafferUI.ScrolledContainer.ScrollMode.Never,
					verticalMode = GafferUI.ScrolledContainer.ScrollMode.Automatic,
					borderWidth = 8
				)

				co = GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Vertical, spacing=NodeUI._columnSpacing )
				sc.setChild( co )

				self.__prevColumn = self.__nodeUI._NodeUI__currentColumn

				self.__nodeUI._NodeUI__currentColumn.append( sc )
				self.__nodeUI._NodeUI__currentColumn = co

			def __exit__( self, type, value, traceBack ) :

				self.__nodeUI._NodeUI__currentColumn = self.__prevColumn

		return ScrollableContext( self )

	def _collapsible( self, **kw ) :

		class CollapsibleContext() :

			def __init__( self, nodeUI, collapsibleKeywords ) :

				self.__nodeUI = nodeUI
				self.__collapsibleKeywords = collapsibleKeywords

			def __enter__( self ) :

				cl = GafferUI.Collapsible( **self.__collapsibleKeywords )
				co = GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Vertical, spacing=NodeUI._columnSpacing )
				cl.setChild( co )

				self.__prevColumn = self.__nodeUI._NodeUI__currentColumn

				self.__nodeUI._NodeUI__currentColumn.append( cl )
				self.__nodeUI._NodeUI__currentColumn = co

			def __exit__( self, type, value, traceBack ) :

				self.__nodeUI._NodeUI__currentColumn = self.__prevColumn

		return CollapsibleContext( self, kw )

	def _addWidget( self, widget ) :

		self.__currentColumn.append( widget )

	def _addPlugWidget( self, plugOrPlugPath ) :

		if isinstance( plugOrPlugPath, basestring ) :
			plug = self._node().getChild( plugOrPlugPath )
		else :
			plug = plugOrPlugPath

		vw = GafferUI.PlugValueWidget.create( plug )
		if vw :
			w = GafferUI.PlugWidget( vw )
			self._addWidget( w )

	## This method is called from the constructor to build the ui. It is
	# intended to be overriden in derived classes.
	def _build( self ) :

		with self._scrollable() :
			self.__buildWalk( self._node() )

	def __buildWalk( self, parent ) :

		plugs = [ x for x in parent.children() if x.isInstanceOf( Gaffer.Plug.staticTypeId() ) ]
		plugs = [ x for x in plugs if x.direction()==Gaffer.Plug.Direction.In and not x.getName().startswith( "__" ) ]
		for plug in plugs :

			if plug.typeId()==Gaffer.CompoundPlug.staticTypeId() :

				with self._collapsible( label = IECore.CamelCase.toSpaced( plug.getName() ), collapsed=True ) :
					self.__buildWalk( plug )

			else :

				self._addPlugWidget( plug )

	@staticmethod
	def _registerPlugWidget( self, nodeTypeId, plugPath, widgetType, **kw ) :

		raise NotImplementedError

	## Creates a NodeUI instance for the specified node.
	@classmethod
	def create( cls, node ) :

		nodeHierarchy = IECore.RunTimeTyped.baseTypeIds( node.typeId() )
		for typeId in [ node.typeId() ] + nodeHierarchy :
			nodeUI = cls.__nodeUIs.get( typeId, None )
			if nodeUI is not None :
				return nodeUI( node )

		assert( 0 )

	__nodeUIs = {}
	## Registers a subclass of NodeUI to be used with a specific node type.
	@classmethod
	def registerNodeUI( cls, nodeTypeId, nodeUIType ) :

		assert( issubclass( nodeUIType, NodeUI ) )

		cls.__nodeUIs[nodeTypeId] = nodeUIType

NodeUI.registerNodeUI( Gaffer.Node.staticTypeId(), NodeUI )
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

from datetime import date
import functools

from openfisca_core.columns import (AgeCol, BoolCol, build_column, DateCol, EnumCol, FixedStrCol, FloatCol, IntCol,
    PeriodSizeIndependentIntCol, StrCol)
from openfisca_core.enumerations import Enum
from openfisca_core.formulas import (calculate_output_add, calculate_output_add_divide, calculate_output_divide,
    dated_function, DatedVariable, EntityToPersonColumn, last_duration_last_value, missing_value, PersonToEntityColumn,
    requested_period_added_value, requested_period_default_value, requested_period_last_value,
    set_input_dispatch_by_period, set_input_divide_by_period, Variable)
from openfisca_core.formula_helpers import apply_thresholds, switch

from ..entities import entity_class_by_symbol, Familles, FoyersFiscaux, Individus, Menages

__all__ = [
    'AgeCol',
    'apply_thresholds',
    'build_column',
    'BoolCol',
    'calculate_output_add',
    'calculate_output_add_divide',
    'calculate_output_divide',
    'CAT',
    'CHEF',
    'CONJ',
    'CREF',
    'date',
    'DateCol',
    'dated_function',
    'DatedVariable',
    'ENFS',
    'EntityToPersonColumn',
    'Enum',
    'EnumCol',
    'Familles',
    'FixedStrCol',
    'FloatCol',
    'FoyersFiscaux',
    'Individus',
    'IntCol',
    'last_duration_last_value',
    'Menages',
    'missing_value',
    'PAC1',
    'PAC2',
    'PAC3',
    'PART',
    'PeriodSizeIndependentIntCol',
    'PersonToEntityColumn',
    'PREF',
    'QUIFAM',
    'QUIFOY',
    'QUIMEN',
    'requested_period_added_value',
    'requested_period_default_value',
    'requested_period_last_value',
    'set_input_dispatch_by_period',
    'set_input_divide_by_period',
    'switch',
    'Variable',
    'StrCol',
    'TAUX_DE_PRIME',
    'VOUS',
    ]

CAT = Enum([
    'prive_non_cadre',
    'prive_cadre',
    'public_titulaire_etat',
    'public_titulaire_militaire',
    'public_titulaire_territoriale',
    'public_titulaire_hospitaliere',
    'public_non_titulaire',
    ])

TAUX_DE_PRIME = 1 / 4  # primes_fonction_publique (hors suppl. familial et indemnité de résidence)/rémunération brute

QUIFAM = Enum(['chef', 'part', 'enf1', 'enf2', 'enf3', 'enf4', 'enf5', 'enf6', 'enf7', 'enf8', 'enf9'])
QUIFOY = Enum(['vous', 'conj', 'pac1', 'pac2', 'pac3', 'pac4', 'pac5', 'pac6', 'pac7', 'pac8', 'pac9'])
QUIMEN = Enum(['pref', 'cref', 'enf1', 'enf2', 'enf3', 'enf4', 'enf5', 'enf6', 'enf7', 'enf8', 'enf9'])

CHEF = QUIFAM['chef']
CONJ = QUIFOY['conj']
CREF = QUIMEN['cref']
ENFS = [
    QUIFAM['enf1'], QUIFAM['enf2'], QUIFAM['enf3'], QUIFAM['enf4'], QUIFAM['enf5'], QUIFAM['enf6'], QUIFAM['enf7'],
    QUIFAM['enf8'], QUIFAM['enf9'],
    ]
PAC1 = QUIFOY['pac1']
PAC2 = QUIFOY['pac2']
PAC3 = QUIFOY['pac3']
PART = QUIFAM['part']
PREF = QUIMEN['pref']
VOUS = QUIFOY['vous']

# Functions and decorators

build_column = functools.partial(
    build_column,
    entity_class_by_symbol = entity_class_by_symbol,
    )
<commit_msg>new base function<commit_after># -*- coding: utf-8 -*-

from datetime import date
import functools

from openfisca_core.columns import (AgeCol, BoolCol, build_column, DateCol, EnumCol, FixedStrCol, FloatCol, IntCol,
    PeriodSizeIndependentIntCol, StrCol)
from openfisca_core.enumerations import Enum
from openfisca_core.formulas import (calculate_output_add, calculate_output_add_divide, calculate_output_divide,
    dated_function, DatedVariable, EntityToPersonColumn, missing_value, PersonToEntityColumn,
    set_input_dispatch_by_period, set_input_divide_by_period, Variable)
from openfisca_core.base_functions import last_duration_last_value,requested_period_added_value,requested_period_default_value,requested_period_last_value, requested_period_last_or_next_value
from openfisca_core.formula_helpers import apply_thresholds, switch

from ..entities import entity_class_by_symbol, Familles, FoyersFiscaux, Individus, Menages

__all__ = [
    'AgeCol',
    'apply_thresholds',
    'build_column',
    'BoolCol',
    'calculate_output_add',
    'calculate_output_add_divide',
    'calculate_output_divide',
    'CAT',
    'CHEF',
    'CONJ',
    'CREF',
    'date',
    'DateCol',
    'dated_function',
    'DatedVariable',
    'ENFS',
    'EntityToPersonColumn',
    'Enum',
    'EnumCol',
    'Familles',
    'FixedStrCol',
    'FloatCol',
    'FoyersFiscaux',
    'Individus',
    'IntCol',
    'last_duration_last_value',
    'Menages',
    'missing_value',
    'PAC1',
    'PAC2',
    'PAC3',
    'PART',
    'PeriodSizeIndependentIntCol',
    'PersonToEntityColumn',
    'PREF',
    'QUIFAM',
    'QUIFOY',
    'QUIMEN',
    'requested_period_added_value',
    'requested_period_default_value',
    'requested_period_last_or_next_value',
    'requested_period_last_value',
    'set_input_dispatch_by_period',
    'set_input_divide_by_period',
    'switch',
    'Variable',
    'StrCol',
    'TAUX_DE_PRIME',
    'VOUS',
    ]

CAT = Enum([
    'prive_non_cadre',
    'prive_cadre',
    'public_titulaire_etat',
    'public_titulaire_militaire',
    'public_titulaire_territoriale',
    'public_titulaire_hospitaliere',
    'public_non_titulaire',
    ])

TAUX_DE_PRIME = 1 / 4  # primes_fonction_publique (hors suppl. familial et indemnité de résidence)/rémunération brute

QUIFAM = Enum(['chef', 'part', 'enf1', 'enf2', 'enf3', 'enf4', 'enf5', 'enf6', 'enf7', 'enf8', 'enf9'])
QUIFOY = Enum(['vous', 'conj', 'pac1', 'pac2', 'pac3', 'pac4', 'pac5', 'pac6', 'pac7', 'pac8', 'pac9'])
QUIMEN = Enum(['pref', 'cref', 'enf1', 'enf2', 'enf3', 'enf4', 'enf5', 'enf6', 'enf7', 'enf8', 'enf9'])

CHEF = QUIFAM['chef']
CONJ = QUIFOY['conj']
CREF = QUIMEN['cref']
ENFS = [
    QUIFAM['enf1'], QUIFAM['enf2'], QUIFAM['enf3'], QUIFAM['enf4'], QUIFAM['enf5'], QUIFAM['enf6'], QUIFAM['enf7'],
    QUIFAM['enf8'], QUIFAM['enf9'],
    ]
PAC1 = QUIFOY['pac1']
PAC2 = QUIFOY['pac2']
PAC3 = QUIFOY['pac3']
PART = QUIFAM['part']
PREF = QUIMEN['pref']
VOUS = QUIFOY['vous']

# Functions and decorators

build_column = functools.partial(
    build_column,
    entity_class_by_symbol = entity_class_by_symbol,
    )
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

from openfisca_core.model_api import *
from openfisca_france.entities import Famille, FoyerFiscal, Individu, Menage

class TypesAAHNonCalculable(Enum):
    calculable = u"Calculable"
    intervention_CDAPH_necessaire = u"intervention_CDAPH_necessaire"

class TypesActivite(Enum):
    actif = u'Actif occupé'
    chomeur = u'Chômeur'
    etudiant = u'Étudiant, élève'
    retraite = u'Retraité'
    inactif = u'Autre, inactif'

class TypesAideLogementNonCalculable(Enum):
    calculable = u"Calculable"
    locataire_foyer = u"Non calculable (Locataire foyer)"

class TypesAllegementCotisationAllocationsFamilialesModeRecouvrement(Enum):
    fin_d_annee = u"fin_d_annee"
    anticipe = u"anticipe_regularisation_fin_de_periode"
    progressif = u"progressif"

class TypesAllegementFillonModeRecouvrement(Enum):
    fin_d_annee = u"fin_d_annee"
    anticipe = u"anticipe_regularisation_fin_de_periode"
    progressif = u"progressif"

class TypesCategorieSalarie(Enum):
    prive_non_cadre = u'prive_non_cadre'
    prive_cadre = u'prive_cadre'
    public_titulaire_etat = u'public_titulaire_etat'
    public_titulaire_militaire = u'public_titulaire_militaire'
    public_titulaire_territoriale = u'public_titulaire_territoriale'
    public_titulaire_hospitaliere = u'public_titulaire_hospitaliere'
    public_non_titulaire = u'public_non_titulaire'
    non_pertinent = u'non_pertinent'

class TypesContratDeTravail(Enum):
    temps_plein = u"temps_plein"
    temps_partiel = u"temps_partiel"
    forfait_heures_semaines = u"forfait_heures_semaines"
    forfait_heures_mois = u"forfait_heures_mois"
    forfait_heures_annee = u"forfait_heures_annee"
    forfait_jours_annee = u"forfait_jours_annee"
    sans_objet = u"sans_objet"

class TypesContratDeTravailDuree(Enum):
    cdi = u"CDI"
    cdd = u"CDD"

class TypesTauxCSGRemplacement(Enum):
    non_renseigne = u"Non renseigné/non pertinent"
    exonere = u"Exonéré"
    taux_reduit = u"Taux réduit"
    taux_plein = u"Taux plein"

class TypesCotisationSocialeModeRecouvrement(Enum):
    mensuel = u"Mensuel avec régularisation en fin d'année"
    annuel = u"Annuel"
    mensuel_strict = u"Mensuel strict"

class TypesEligibiliteANAH(Enum):
    a_verifier = u"A vérifier"
    modestes = u"Modestes"
    tres_modeste = u"Très modestes"

class TypesExpositionAccident(Enum):
    faible = u"Faible"
    moyen = u"Moyen"
    eleve = u"Élevé"
    tres_eleve = u"Très élevé"

class TypesExpositionPenibilite(Enum):
    nulle = u"Nulle, pas d'exposition de l'employé à un facteur de pénibilité"
    simple = u"Simple, exposition à un seul facteur de pénibilité"
    multiple = u"Multiple, exposition à plusieurs facteurs de pénibilité"

class TypesGir(Enum):
    non_defini = u"Non défini"
    gir_1 = u"Gir 1"
    gir_2 = u"Gir 2"
    gir_3 = u"Gir 3"
    gir_4 = u"Gir 4"
    gir_5 = u"Gir 5"
    gir_6 = u"Gir 6"

class TypesRSANonCalculable(Enum):
    calculable = u"Calculable"
    tns = u"tns"
    conjoint_tns = u"conjoint_tns"

class TypesScolarite(Enum):
    inconnue = u"Inconnue"
    college = u"Collège"
    lycee = u"Lycée"

class TypesStatutMarital(Enum):
    non_renseigne = u'Non renseigné'
    marie = u'Marié'
    celibataire = u'Celibataire'
    divorce = u'Divorcé'
    veuf = u'Veuf'
    pacse = u'Pacsé'
    jeune_veuf = u'Jeune veuf'

class TypesStatutOccupationLogement(Enum):
    non_renseigne = u"Non renseigné"
    primo_accedant = u"Accédant à la propriété"
    proprietaire = u"Propriétaire (non accédant) du logement"
    locataire_hlm = u"Locataire d'un logement HLM"
    locataire_vide = u"Locataire ou sous-locataire d'un logement loué vide non-HLM"
    locataire_meuble = u"Locataire ou sous-locataire d'un logement loué meublé ou d'une chambre d'hôtel"
    loge_gratuitement = u"Logé gratuitement par des parents, des amis ou l'employeur"
    locataire_foyer = u"Locataire d'un foyer (résidence universitaire, maison de retraite, foyer de jeune travailleur, résidence sociale...)"
    sans_domicile = u"Sans domicile stable"

class TypesTailleEntreprise(Enum):
    non_pertinent = u"Non pertinent"
    moins_de_10 = u"Moins de 10 salariés"
    de_10_a_19 = u"De 10 à 19 salariés"
    de_20_a_249 = u"De 20 à 249 salariés"
    plus_de_250 = u"Plus de 250 salariés"

class TypesTnsTypeActivite(Enum):
    achat_revente = u'achat_revente'
    bic = u'bic'
    bnc = u'bnc'

class TypesZoneApl(Enum) :
    non_renseigne = u"Non renseigné"
    zone_1 = u"Zone 1"
    zone_2 = u"Zone 2"
    zone_3 = u"Zone 3"

TAUX_DE_PRIME = 1 / 4  # primes_fonction_publique (hors suppl. familial et indemnité de résidence)/rémunération brute

# Legacy roles. To be removed when they are not used by formulas anymore.
class QUIFAM(Enum):
    chef = 0
    part = 1
    enf1 = 2
    enf2 = 3
    enf3 = 4
    enf4 = 5
    enf5 = 6
    enf6 = 7
    enf7 = 8
    enf8 = 9
    enf9 = 10

class QUIFOY(Enum):
    vous = 0
    conj = 1
    pac1 = 2
    pac2 = 3
    pac3 = 4
    pac4 = 5
    pac5 = 6
    pac6 = 7
    pac7 = 8
    pac8 = 9
    pac9 = 10

class QUIMEN(Enum):
    pref = 0
    cref = 1
    enf1 = 2
    enf2 = 3
    enf3 = 4
    enf4 = 5
    enf5 = 6
    enf6 = 7
    enf7 = 8
    enf8 = 9
    enf9 = 10

CHEF = QUIFAM.chef.value
CONJ = QUIFOY.conj.value
CREF = QUIMEN.cref.value
ENFS = [
    QUIFAM.enf1.value, QUIFAM.enf2.value, QUIFAM.enf3.value, QUIFAM.enf4.value, QUIFAM.enf5.value, QUIFAM.enf6.value, QUIFAM.enf7.value,
    QUIFAM.enf8.value, QUIFAM.enf9.value,
    ]
PAC1 = QUIFOY.pac1.value
PAC2 = QUIFOY.pac2.value
PAC3 = QUIFOY.pac3.value
PART = QUIFAM.part.value
PREF = QUIMEN.pref.value
VOUS = QUIFOY.vous.value
<commit_msg>Add __order__ to enums<commit_after># -*- coding: utf-8 -*-

from openfisca_core.model_api import *
from openfisca_france.entities import Famille, FoyerFiscal, Individu, Menage

# Enums used through the legislation
# The __order__ is only necessary because of Python 2

class TypesAAHNonCalculable(Enum):
    __order__ = 'calculable intervention_CDAPH_necessaire'
    calculable = u"Calculable"
    intervention_CDAPH_necessaire = u"intervention_CDAPH_necessaire"

class TypesActivite(Enum):
    __order__ = 'actif chomeur etudiant retraite inactif'
    actif = u'Actif occupé'
    chomeur = u'Chômeur'
    etudiant = u'Étudiant, élève'
    retraite = u'Retraité'
    inactif = u'Autre, inactif'

class TypesAideLogementNonCalculable(Enum):
    __order__ = 'calculable locataire_foyer'
    calculable = u"Calculable"
    locataire_foyer = u"Non calculable (Locataire foyer)"

class TypesAllegementCotisationAllocationsFamilialesModeRecouvrement(Enum):
    __order__ = 'fin_d_annee anticipe progressif'
    fin_d_annee = u"fin_d_annee"
    anticipe = u"anticipe_regularisation_fin_de_periode"
    progressif = u"progressif"

# TODO: Merge with the previous one ?
class TypesAllegementFillonModeRecouvrement(Enum):
    __order__ = 'fin_d_annee anticipe progressif'
    fin_d_annee = u"fin_d_annee"
    anticipe = u"anticipe_regularisation_fin_de_periode"
    progressif = u"progressif"

class TypesCategorieSalarie(Enum):
    __order__ = 'prive_non_cadre prive_cadre public_titulaire_etat public_titulaire_militaire public_titulaire_territoriale public_titulaire_hospitaliere public_non_titulaire non_pertinent'
    prive_non_cadre = u'prive_non_cadre'
    prive_cadre = u'prive_cadre'
    public_titulaire_etat = u'public_titulaire_etat'
    public_titulaire_militaire = u'public_titulaire_militaire'
    public_titulaire_territoriale = u'public_titulaire_territoriale'
    public_titulaire_hospitaliere = u'public_titulaire_hospitaliere'
    public_non_titulaire = u'public_non_titulaire'
    non_pertinent = u'non_pertinent'

class TypesContratDeTravail(Enum):
    __order__ = 'temps_plein temps_partiel forfait_heures_semaines forfait_heures_mois forfait_heures_annee forfait_jours_annee sans_objet'
    temps_plein = u"temps_plein"
    temps_partiel = u"temps_partiel"
    forfait_heures_semaines = u"forfait_heures_semaines"
    forfait_heures_mois = u"forfait_heures_mois"
    forfait_heures_annee = u"forfait_heures_annee"
    forfait_jours_annee = u"forfait_jours_annee"
    sans_objet = u"sans_objet"

class TypesContratDeTravailDuree(Enum):
    __order__ = 'cdi cdd'
    cdi = u"CDI"
    cdd = u"CDD"

class TypesTauxCSGRemplacement(Enum):
    __order__ = 'non_renseigne exonere taux_reduit taux_plein'
    non_renseigne = u"Non renseigné/non pertinent"
    exonere = u"Exonéré"
    taux_reduit = u"Taux réduit"
    taux_plein = u"Taux plein"

class TypesCotisationSocialeModeRecouvrement(Enum):
    __order__ = 'mensuel annuel mensuel_strict'
    mensuel = u"Mensuel avec régularisation en fin d'année"
    annuel = u"Annuel"
    mensuel_strict = u"Mensuel strict"

class TypesEligibiliteANAH(Enum):
    __order__ = 'a_verifier modestes tres_modeste'
    a_verifier = u"A vérifier"
    modestes = u"Modestes"
    tres_modeste = u"Très modestes"

class TypesExpositionAccident(Enum):
    __order__ = 'faible moyen eleve tres_eleve'
    faible = u"Faible"
    moyen = u"Moyen"
    eleve = u"Élevé"
    tres_eleve = u"Très élevé"

class TypesExpositionPenibilite(Enum):
    __order__ = 'nulle simple multiple'
    nulle = u"Nulle, pas d'exposition de l'employé à un facteur de pénibilité"
    simple = u"Simple, exposition à un seul facteur de pénibilité"
    multiple = u"Multiple, exposition à plusieurs facteurs de pénibilité"

class TypesGir(Enum):
    __order__ = 'gir_1 gir_2 gir_3 gir_4 gir_5 gir_6'
    non_defini = u"Non défini"
    gir_1 = u"Gir 1"
    gir_2 = u"Gir 2"
    gir_3 = u"Gir 3"
    gir_4 = u"Gir 4"
    gir_5 = u"Gir 5"
    gir_6 = u"Gir 6"

class TypesRSANonCalculable(Enum):
    __order__ = 'calculable tns conjoint_tns'
    calculable = u"Calculable"
    tns = u"tns"
    conjoint_tns = u"conjoint_tns"

class TypesScolarite(Enum):
    __order__ = 'inconnue college lycee'
    inconnue = u"Inconnue"
    college = u"Collège"
    lycee = u"Lycée"

class TypesStatutMarital(Enum):
    __order__ = 'non_renseigne marie celibataire divorce veuf pacse jeune_veuf'
    non_renseigne = u'Non renseigné'
    marie = u'Marié'
    celibataire = u'Celibataire'
    divorce = u'Divorcé'
    veuf = u'Veuf'
    pacse = u'Pacsé'
    jeune_veuf = u'Jeune veuf'

class TypesStatutOccupationLogement(Enum):
    __order__ = 'non_renseigne primo_accedant proprietaire locataire_hlm locataire_vide locataire_meuble loge_gratuitement locataire_foyer sans_domicile'
    non_renseigne = u"Non renseigné"
    primo_accedant = u"Accédant à la propriété"
    proprietaire = u"Propriétaire (non accédant) du logement"
    locataire_hlm = u"Locataire d'un logement HLM"
    locataire_vide = u"Locataire ou sous-locataire d'un logement loué vide non-HLM"
    locataire_meuble = u"Locataire ou sous-locataire d'un logement loué meublé ou d'une chambre d'hôtel"
    loge_gratuitement = u"Logé gratuitement par des parents, des amis ou l'employeur"
    locataire_foyer = u"Locataire d'un foyer (résidence universitaire, maison de retraite, foyer de jeune travailleur, résidence sociale...)"
    sans_domicile = u"Sans domicile stable"

class TypesTailleEntreprise(Enum):
    __order__ = 'non_pertinent moins_de_10 de_10_a_19 de_20_a_249 plus_de_250'
    non_pertinent = u"Non pertinent"
    moins_de_10 = u"Moins de 10 salariés"
    de_10_a_19 = u"De 10 à 19 salariés"
    de_20_a_249 = u"De 20 à 249 salariés"
    plus_de_250 = u"Plus de 250 salariés"

class TypesTnsTypeActivite(Enum):
    __order__ = 'achat_revente bic bnc'
    achat_revente = u'achat_revente'
    bic = u'bic'
    bnc = u'bnc'

class TypesZoneApl(Enum):
    __order__ = 'non_renseigne zone_1 zone_2 zone_3'
    non_renseigne = u"Non renseigné"
    zone_1 = u"Zone 1"
    zone_2 = u"Zone 2"
    zone_3 = u"Zone 3"

TAUX_DE_PRIME = 1 / 4  # primes_fonction_publique (hors suppl. familial et indemnité de résidence)/rémunération brute

# Legacy roles. To be removed when they are not used by formulas anymore.
class QUIFAM(Enum):
    __order__ = 'chef part enf1 enf2 enf3 enf4 enf5 enf6 enf7 enf8 enf9'
    chef = 0
    part = 1
    enf1 = 2
    enf2 = 3
    enf3 = 4
    enf4 = 5
    enf5 = 6
    enf6 = 7
    enf7 = 8
    enf8 = 9
    enf9 = 10

class QUIFOY(Enum):
    __order__ = 'vous conj pac1 pac2 pac3 pac4 pac5 pac6 pac7 pac8 pac9'
    vous = 0
    conj = 1
    pac1 = 2
    pac2 = 3
    pac3 = 4
    pac4 = 5
    pac5 = 6
    pac6 = 7
    pac7 = 8
    pac8 = 9
    pac9 = 10

class QUIMEN(Enum):
    __order__ = 'pref cref enf1 enf2 enf3 enf4 enf5 enf6 enf7 enf8 enf9'
    pref = 0
    cref = 1
    enf1 = 2
    enf2 = 3
    enf3 = 4
    enf4 = 5
    enf5 = 6
    enf6 = 7
    enf7 = 8
    enf8 = 9
    enf9 = 10

CHEF = QUIFAM.chef.value
CONJ = QUIFOY.conj.value
CREF = QUIMEN.cref.value
ENFS = [
    QUIFAM.enf1.value, QUIFAM.enf2.value, QUIFAM.enf3.value, QUIFAM.enf4.value, QUIFAM.enf5.value, QUIFAM.enf6.value, QUIFAM.enf7.value,
    QUIFAM.enf8.value, QUIFAM.enf9.value,
    ]
PAC1 = QUIFOY.pac1.value
PAC2 = QUIFOY.pac2.value
PAC3 = QUIFOY.pac3.value
PART = QUIFAM.part.value
PREF = QUIMEN.pref.value
VOUS = QUIFOY.vous.value
<|endoftext|>


<commit_before>__all__ = ('Logical', 'Integer', 'Number', 'String', 'TextFile', 'PDF_File',
    'PNG_Image', 'RExpression', 'List', 'Vector')

import copy
from t2util import BeanshellEscapeString
from t2activity import BeanshellActivity

class T2FlowType:

    def __init__(self, depth=0):
        self.depth = depth
        self.dict = {}

    def __call__(self, **kw):
        new = copy.copy(self)
        new.dict = kw
        return new

    def getDomain(self):
        # return all possible values of this type as a (frozen) set of strings.
        # Return None if domain is too large or not possible to represent.
        return None

    def getDepth(self):
        return self.depth

    def validator(self, inputType):
        return None

class StringType(T2FlowType):

    def __init__(self, domain=None):
        T2FlowType.__init__(self)
        self.domain = domain

    def __getitem__(self, domain):
        if isinstance(domain, tuple):
            return StringType(domain)
        else:
            return StringType((domain,))

    def symanticType(self):
        return 'STRING'

    def getDomain(self):
        if self.domain is not None:
            return frozenset(self.domain)
        else:
            return None

    def validator(self, inputType):
        if isinstance(inputType, ListType):
            inputType = inputType.baseType
        if self.domain is None:
            # this string doesn't care what value it has, so do not validate
            return None
        inputDomain = inputType.getDomain()
        if inputDomain is not None and inputDomain <= self.getDomain():
            # the input string must be one of our valid strings, so do not validate
            return None
        script = "switch(input)\n{\n"
        for value in self.domain:
            script += 'case "%s":\n' % BeanshellEscapeString(value)
        script += """\toutput = input;
\tbreak;
default:
\tthrow RuntimeException("Invalid value");
}
"""
        return BeanshellActivity(script, inputs={'input': T2FlowType()},
            outputs={'output': T2FlowType()})

String = StringType()

class LogicalType(T2FlowType):

    domain = ('FALSE', 'TRUE')
    domainSet = frozenset(domain)

    def __init__(self):
        T2FlowType.__init__(self)

    def getDomain(self):
        return self.domainSet

    def symanticType(self):
        return 'BOOL'

    def validator(self, inputType):
        return StringType(self.domain).validator(inputType)

Logical = LogicalType()

class IntegerType(T2FlowType):

    def __init__(self, lower=None, higher=None):
        T2FlowType.__init__(self)
        if lower is not None and higher is not None and lower > higher:
            raise RuntimeError('lower bound greater than upper bound')
        self.lower = lower
        self.higher = higher

    def symanticType(self):
        return 'INTEGER'

    def __getitem__(self, domain):
        if not isinstance(domain, tuple):
            raise RuntimeError('unrecognised Integer range')
        if len(domain) == 2:
            if domain[0] == Ellipsis:
                lower = None
                higher = domain[1]
            elif domain[1] == Ellipsis:
                lower = domain[0]
                higher = None
            else:
                raise RuntimeError('unrecognised Integer range')
        elif len(domain) == 3 and domain[1] == Ellipsis:
            lower = domain[0]
            higher = domain[2]
        else:
            raise RuntimeError('unrecognised Integer range')
        return IntegerType(lower, higher)

    def validator(self, inputType):
        if isinstance(inputType, ListType):
            inputType = inputType.baseType
        if isinstance(inputType, StringType):
            script = "output = Integer.parseInt(String.trim(input));\n"
        elif isinstance(inputType, NumberType):
            script = "output = Integer.parseInt(String.trim(input));\n" # should round somehow
        elif isinstance(inputType, IntegerType):
            script = ""
        if self.lower is None:
            if self.higher is None:
                condition = None
            else:
                condition = "output > %d" % self.higher
        else:
            if self.higher is None:
                condition = "output < %d" % self.lower
            else:
                condition = "output < %d || output > %d" % (self.lower, self.higher)
        if condition is not None:
            script += 'if (%s) {\n  throw new RuntimeException("integer out of bounds");\n}\n' % self.condition
        if script:
            return BeanshellActivity(script, inputs={'input': T2FlowType()}, outputs={'output': T2FlowType()})

Integer = IntegerType()

class NumberType(T2FlowType):

    def symanticType(self):
        return 'DOUBLE'

Number = NumberType()
class PNG_ImageType(T2FlowType):

    def symanticType(self):
        return 'PNG_FILE'

PNG_Image = PNG_ImageType()

PDF_File = PNG_Image

class TextFileType(T2FlowType):

    def symanticType(self):
        return 'TEXT_FILE'

TextFile = TextFileType()

VectorBaseType = {
    Logical: 'BOOL_LIST',
    Integer: 'INTEGER_LIST',
    Number: 'DOUBLE_LIST',
    String: 'STRING_LIST'
}

class ListType(T2FlowType):

    def __init__(self, elementType, depth=None):
        if depth is not None:
            self.baseType = elementType
            depth = depth
        elif isinstance(elementType, ListType):
            self.baseType = elementType.baseType
            depth = elementType.depth + 1
        else:
            self.baseType = elementType
            depth = elementType.depth + 1
        T2FlowType.__init__(self, depth)

    def validator(self, inputType):
        return self.baseType.validator(inputType)

    def symanticType(self):
        return VectorBaseType[self.baseType]

class ListFactory:

    def __getitem__(self, elementType):
        return ListType(elementType)

List = ListFactory()

class VectorFactory:

    def __getitem__(self, elementType):
        if isinstance(elementType, ListType):
            raise RuntimeError('Vector can not have depth > 1')
        if not VectorBaseType.has_key(elementType):
            raise RuntimeError('Invalid Vector type')
        return ListType(elementType)

Vector = VectorFactory()

class RExpressionType(T2FlowType):

    def __init__(self):
        T2FlowType.__init__(self, 1)

    def symanticType(self):
        return 'R_EXP'

RExpression = RExpressionType()
<commit_msg>Fixed reference to field instead of local<commit_after>__all__ = ('Logical', 'Integer', 'Number', 'String', 'TextFile', 'PDF_File',
    'PNG_Image', 'RExpression', 'List', 'Vector')

import copy
from t2util import BeanshellEscapeString
from t2activity import BeanshellActivity

class T2FlowType:

    def __init__(self, depth=0):
        self.depth = depth
        self.dict = {}

    def __call__(self, **kw):
        new = copy.copy(self)
        new.dict = kw
        return new

    def getDomain(self):
        # return all possible values of this type as a (frozen) set of strings.
        # Return None if domain is too large or not possible to represent.
        return None

    def getDepth(self):
        return self.depth

    def validator(self, inputType):
        return None

class StringType(T2FlowType):

    def __init__(self, domain=None):
        T2FlowType.__init__(self)
        self.domain = domain

    def __getitem__(self, domain):
        if isinstance(domain, tuple):
            return StringType(domain)
        else:
            return StringType((domain,))

    def symanticType(self):
        return 'STRING'

    def getDomain(self):
        if self.domain is not None:
            return frozenset(self.domain)
        else:
            return None

    def validator(self, inputType):
        if isinstance(inputType, ListType):
            inputType = inputType.baseType
        if self.domain is None:
            # this string doesn't care what value it has, so do not validate
            return None
        inputDomain = inputType.getDomain()
        if inputDomain is not None and inputDomain <= self.getDomain():
            # the input string must be one of our valid strings, so do not validate
            return None
        script = "switch(input)\n{\n"
        for value in self.domain:
            script += 'case "%s":\n' % BeanshellEscapeString(value)
        script += """\toutput = input;
\tbreak;
default:
\tthrow RuntimeException("Invalid value");
}
"""
        return BeanshellActivity(script, inputs={'input': T2FlowType()},
            outputs={'output': T2FlowType()})

String = StringType()

class LogicalType(T2FlowType):

    domain = ('FALSE', 'TRUE')
    domainSet = frozenset(domain)

    def __init__(self):
        T2FlowType.__init__(self)

    def getDomain(self):
        return self.domainSet

    def symanticType(self):
        return 'BOOL'

    def validator(self, inputType):
        return StringType(self.domain).validator(inputType)

Logical = LogicalType()

class IntegerType(T2FlowType):

    def __init__(self, lower=None, higher=None):
        T2FlowType.__init__(self)
        if lower is not None and higher is not None and lower > higher:
            raise RuntimeError('lower bound greater than upper bound')
        self.lower = lower
        self.higher = higher

    def symanticType(self):
        return 'INTEGER'

    def __getitem__(self, domain):
        if not isinstance(domain, tuple):
            raise RuntimeError('unrecognised Integer range')
        if len(domain) == 2:
            if domain[0] == Ellipsis:
                lower = None
                higher = domain[1]
            elif domain[1] == Ellipsis:
                lower = domain[0]
                higher = None
            else:
                raise RuntimeError('unrecognised Integer range')
        elif len(domain) == 3 and domain[1] == Ellipsis:
            lower = domain[0]
            higher = domain[2]
        else:
            raise RuntimeError('unrecognised Integer range')
        return IntegerType(lower, higher)

    def validator(self, inputType):
        if isinstance(inputType, ListType):
            inputType = inputType.baseType
        if isinstance(inputType, StringType):
            script = "output = Integer.parseInt(String.trim(input));\n"
        elif isinstance(inputType, NumberType):
            script = "output = Integer.parseInt(String.trim(input));\n" # should round somehow
        elif isinstance(inputType, IntegerType):
            script = ""
        if self.lower is None:
            if self.higher is None:
                condition = None
            else:
                condition = "output > %d" % self.higher
        else:
            if self.higher is None:
                condition = "output < %d" % self.lower
            else:
                condition = "output < %d || output > %d" % (self.lower, self.higher)
        if condition is not None:
            script += 'if (%s) {\n  throw new RuntimeException("integer out of bounds");\n}\n' % condition
        if script:
            return BeanshellActivity(script, inputs={'input': T2FlowType()}, outputs={'output': T2FlowType()})

Integer = IntegerType()

class NumberType(T2FlowType):

    def symanticType(self):
        return 'DOUBLE'

Number = NumberType()
class PNG_ImageType(T2FlowType):

    def symanticType(self):
        return 'PNG_FILE'

PNG_Image = PNG_ImageType()

PDF_File = PNG_Image

class TextFileType(T2FlowType):

    def symanticType(self):
        return 'TEXT_FILE'

TextFile = TextFileType()

VectorBaseType = {
    Logical: 'BOOL_LIST',
    Integer: 'INTEGER_LIST',
    Number: 'DOUBLE_LIST',
    String: 'STRING_LIST'
}

class ListType(T2FlowType):

    def __init__(self, elementType, depth=None):
        if depth is not None:
            self.baseType = elementType
            depth = depth
        elif isinstance(elementType, ListType):
            self.baseType = elementType.baseType
            depth = elementType.depth + 1
        else:
            self.baseType = elementType
            depth = elementType.depth + 1
        T2FlowType.__init__(self, depth)

    def validator(self, inputType):
        return self.baseType.validator(inputType)

    def symanticType(self):
        return VectorBaseType[self.baseType]

class ListFactory:

    def __getitem__(self, elementType):
        return ListType(elementType)

List = ListFactory()

class VectorFactory:

    def __getitem__(self, elementType):
        if isinstance(elementType, ListType):
            raise RuntimeError('Vector can not have depth > 1')
        if not VectorBaseType.has_key(elementType):
            raise RuntimeError('Invalid Vector type')
        return ListType(elementType)

Vector = VectorFactory()

class RExpressionType(T2FlowType):

    def __init__(self):
        T2FlowType.__init__(self, 1)

    def symanticType(self):
        return 'R_EXP'

RExpression = RExpressionType()
<|endoftext|>