# Relationship between variables

**URL:** <https://discourse.julialang.org/t/relationship-between-variables/51272>\
**Category:** Optimization (Mathematical)\
**Tags:** jump, optimization\
**Created:** [December 4, 2020, 8:26pm UTC](https://discourse.julialang.org/t/relationship-between-variables/51272 "2020-12-04T20:26:28Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![JohnZ](https://avatars.discourse-cdn.com/v4/letter/j/ba8739/32.png) [@JohnZ](https://discourse.julialang.org/u/JohnZ)\
**Post date:** [December 4, 2020, 8:26pm UTC](https://discourse.julialang.org/t/relationship-between-variables/51272/1 "2020-12-04T20:26:28Z")

</div>

In an optimisation problem, I have some relationships between variables i.e. they should take same values. I define a dataframe (called Master) that contains all the data and another dataframe (called Relationship) that in each row contains ID for variables that are related. For example, first row contains A1, A5, A3 and A9, so based on indexing in Master a constraint should ensure that x[1] = x[5] = x[3] = x[9]. This is explained in more detail in the MWE in comments. I thought it will be more a DataFrames related issue but I have not been able to get my head around this specific problem.

**How could I define this constraint?**

A not so nice way of applying the constraint could be to arbitrary change the IDs in the `Master` where they are related, i.e. replace with same ID. And then use the `unique` function of DataFrames to find the corresponding index and set them equal, but a more elegant solution should be possible.

```julia
using JuMP, Clp
function get_data()
    Relationship = DataFrame(ID = ["A1","A2","A4"], LINK_1 = ["A5","A6","A10"],
    LINK_2 = ["A3","A7",""], LINK_3 = ["A9","A11","A12"])
    ID_t = collect(1:15)
    ID = Vector{String}(undef,15)
    for i in 1:15
        ID[i]="A".*string(ID_t[i])
    end

    PRICE = rand(10.0:100.0,15)
    Master = DataFrame(ID = ID,PRICE = PRICE)
    return Relationship, Master
end

Relationship, Master = get_data()
Price = Master.PRICE
model = Model(Clp.Optimizer)
@variable(model,0<=x[i=1:length(Price)] <=1)
@constraint(model,con, sum(Price[i]*x[i] for i in 1:length(Price)) <=300)
@constraint(model,con2, sum(Price[i]*x[i] for i in 1:length(Price)) >=200)

# Create a dummy variable so that all elements in same rows in the Relationship dataframe
# take same values (in optimisation)
@variable(model,t[1:length(Relationship.ID)])

# Define the constraint that ensures that 
#x[1] = x[5] = x[3] = x[9] = t[1]
#x[2] = x[6] = x[7] = x[11] = t[2]
#x[4] = x[10] = x[12] = t[3]

# Create a dictionary based on the Master dataframe 
d = Dict([Master.ID[i] => Master.PRICE[i] for i in 1:nrow(Master)])
# For all rows in Relationship get references to the corresponding entries 
# in Master to define the above constraint

 # Warning some of the rows may have some blanks entries as in the example!

```

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [December 4, 2020, 9:55pm UTC](https://discourse.julialang.org/t/relationship-between-variables/51272/2 "2020-12-04T21:55:47Z")

</div>

You want something like

```julia
model = Model()
@variable(model, x[Master.ID])
@variable(model,t[1:length(Relationship.ID)])
for (i, row) in enumerate(eachrow(Relationship))
    for key in values(row)
        if !isempty(key)
            @constraint(model, t[i] == x[key])
        end
    end
end

```

---

<div class="post-metadata">

**Author:** ![JohnZ](https://avatars.discourse-cdn.com/v4/letter/j/ba8739/32.png) [@JohnZ](https://discourse.julialang.org/u/JohnZ)\
**Post date:** [December 5, 2020, 1:54am UTC](https://discourse.julialang.org/t/relationship-between-variables/51272/3 "2020-12-05T01:54:44Z")

</div>

Thank you. I get “KeyError: key 1 not found” . This arises when it gets to constraints and probably because of the JuMP container _DenseAxisArray_.

`@constraint(model,con, sum(Price[i]*x[i] for i in 1:length(Price)) <=300)`  
For this specific example, I can use `@constraint(model,Price'*x <=300) ` but I lose flexibility if I need to use a comprehension, nested comprehension or loop for a more complex constraint.  
I also tried this but it is not equivalent to the constraint in question: `@constraint(model,sum(Price[j] *x[i] for i in keys(x), j in 1:length(Price))<=300)`

Please could you advise on use of comprehension and/or loop with this container type (as in the constraint)? I have given the code that reproduces this error:

```julia
using JuMP, Clp, DataFrames
function get_data()
    Relationship = DataFrame(ID = ["A1","A2","A4"], LINK_1 = ["A5","A6","A10"],
    LINK_2 = ["A3","A7",""], LINK_3 = ["A9","A11","A12"])
    ID_t = collect(1:15)
    ID = Vector{String}(undef,15)
    for i in 1:15
        ID[i]="A".*string(ID_t[i])
    end

    PRICE = rand(10.0:100.0,15)
    Master = DataFrame(ID = ID,PRICE = PRICE)
    return Relationship, Master
end

Relationship, Master = get_data()
Price = Master.PRICE
model = Model(Clp.Optimizer)

@variable(model, 0<=x[Master.ID]<=1)
@variable(model,t[1:length(Relationship.ID)])
for (i, row) in enumerate(eachrow(Relationship))
    for key in values(row)
        if !isempty(key)
            @constraint(model, t[i] == x[key])
        end
    end
end
@constraint(model,con, sum(Price[i]*x[i] for i in 1:length(Price)) <=300)
@constraint(model,con2, sum(Price[i]*x[i] for i in 1:length(Price)) >=200)

optimize!(model)

```

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [December 5, 2020, 2:47am UTC](https://discourse.julialang.org/t/relationship-between-variables/51272/4 "2020-12-05T02:47:14Z")

</div>

I suggest you step back and think about what’s going on here.

```julia
@variable(model, 0<=x[Master.ID]<=1)

```

means that the keys of `x` are now `x["A1"]`, `x["A2"]`, etc, whereas in the constraint, you are trying to index `x[1]`, `x[2]`, etc.

Rather than looping through `i in 1:length(Price)`, you should change the loop to go through the keys of `x`. (Hint: you could use `for (id, price) in zip(Master.ID, Master.PRICE)`.)

Or, you should change the definition of `x` back to what you had originally, and change the `t[i] == x[key]` constraint to look up the integer index from the string `key`.

---

<div class="post-metadata">

**Author:** ![JohnZ](https://avatars.discourse-cdn.com/v4/letter/j/ba8739/32.png) [@JohnZ](https://discourse.julialang.org/u/JohnZ)\
**Post date:** [December 6, 2020, 7:06pm UTC](https://discourse.julialang.org/t/relationship-between-variables/51272/5 "2020-12-06T19:06:13Z")

</div>

Thank you. I have tried to educate myself on dictionaries in Julia over the weekend, and now I have hit a dead end. I was under the impression that I have picked up fair bit of Julia, but struggling with such a basic manipulation has made me to think otherwise.

I have unsuccessfully tried different combinations with `for (id,price) in zip(Master.ID,Master.PRICE)`

I know it is bound to fail if I define the constraint the following way:

`@constraint(model,con,sum(keys(x)*keys(price) for (id,price) in zip(Master.ID,Master.PRICE)<=300)`

`@constraint(model,con,sum(x*price for (id,price) in zip(Master.ID,Master.PRICE)<=300)`

> [@odow](#):
>
> Or, you should change the definition of `x` back to what you had originally, and change the `t[i] == x[key]` constraint to look up the integer index from the string `key` .

This would be my preference but I am not sure how to do it.

Any tips on what else I can try?

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [December 6, 2020, 7:15pm UTC](https://discourse.julialang.org/t/relationship-between-variables/51272/6 "2020-12-06T19:15:55Z")

</div>

```nohighlight
@constraint(
    model, 
    sum(price * x[id] for (id, price) in zip(Master.ID, Master.PRICE)) <= 300
)

```

---

<div class="post-metadata">

**Author:** ![JohnZ](https://avatars.discourse-cdn.com/v4/letter/j/ba8739/32.png) [@JohnZ](https://discourse.julialang.org/u/JohnZ)\
**Post date:** [December 6, 2020, 7:52pm UTC](https://discourse.julialang.org/t/relationship-between-variables/51272/7 "2020-12-06T19:52:34Z")

</div>

Thanks a lot!
