0

I am trying to generate 2 children from 2 parents by crossover. I want to fix a part from parent A and fill the blanks with elements from parent B.

I was able to mask both parents and get the elements on another array, but I am not able to fill in the gaps from the fixed part of Parent A with the fill elements from Parent B

Here's what I have tried so far:

    import numpy as np
    from numpy.random import default_rng
    rng = default_rng()

    numMachines = 5
    numJobs = 5

    population =[[[4, 0, 2, 1, 3],
                    [4, 2, 0, 1, 3],
                    [4, 2, 0, 1, 3],
                    [4, 0, 3, 2, 1],
                    [2, 3, 4, 1, 0]], 

                    [[2, 0, 1, 3, 4],
                    [4, 3, 1, 2, 0],
                    [2, 0, 3, 4, 1],
                    [4, 3, 1, 0, 2],
                    [4, 0, 3, 1, 2]]]

    parentA = np.array(population[0])
    parentB = np.array(population[1])

    childA = np.zeros((numJobs, numMachines))
    np.copyto(childA, parentA)

    childB = np.zeros((numJobs, numMachines))
    np.copyto(childB, parentB)

    subJobs = np.stack([rng.choice(numJobs ,size=int(np.max([2, np.floor(numJobs/2)])), replace=False) for i in range(numMachines)])

    maskA = np.stack([(np.isin(childA[i], subJobs[i])) for i in range(numMachines)])
    invMaskA = np.invert(maskA)

    maskB = np.stack([(np.isin(childB[i], subJobs[i])) for i in range(numMachines)])
    invMaskB = np.invert(maskB)

    maskedChildAFixed = np.ma.masked_array(childA, maskA)
    maskedChildBFixed = np.ma.masked_array(childB, maskB)

    maskedChildAFill = np.ma.masked_array(childA, invMaskA)
    maskedChildBFill = np.ma.masked_array(childB, invMaskB)

    maskedChildAFill = np.stack([maskedChildAFill[i].compressed() for i in range(numMachines)])
    maskedChildBFill = np.stack([maskedChildBFill[i].compressed() for i in range(numMachines)])

EDIT:

Sorry, I was so frustrated with this yesterday that I forgot to add some more information to make it more clear. First, I have fixed the code so it now runs by just copying and pasting (I forgot to add some import calls and some variables).

This is a fixed portion from Parent A that won't change in child A.

>>> print(maskedChildAFixed)
[[-- 0.0 2.0 -- 3.0]
 [4.0 -- 0.0 1.0 --]
 [4.0 -- -- 1.0 3.0]
 [-- 0.0 3.0 2.0 --]
 [-- -- 4.0 1.0 0.0]]

I need to fill in these blank parts with the fill part from parent B.

>>> print(maskedChildBFill)
[[1. 4.]
 [3. 2.]
 [2. 0.]
 [4. 1.]
 [3. 2.]]

For my children to be legal I can't repeat an integer in each row. If I try to use the "np.na.filled()" function with the compressed maskedChildBFill it gives me an error.

>>> print(np.ma.filled(maskedChildAFixed, fill_value=maskedChildBFill)) 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\Rafael\.conda\envs\CoutoBinario\lib\site-packages\numpy\ma\core.py", line 639, in filled
    return a.filled(fill_value)
  File "C:\Users\Rafael\.conda\envs\CoutoBinario\lib\site-packages\numpy\ma\core.py", line 3752, in filled
    np.copyto(result, fill_value, where=m)
  File "<__array_function__ internals>", line 6, in copyto
ValueError: could not broadcast input array from shape (5,2) into shape (5,5)

I'll now coment the part of the code that compresses the fill portion (lines 46 and 47). It won't delete the blank spaces from the maskedChildBFill so that the size of the matrices are preserved.

>>> print(np.ma.filled(maskedChildAFixed, fill_value=maskedChildBFill))
[[2. 0. 2. 3. 3.]
 [4. 3. 0. 1. 0.]
 [4. 0. 3. 1. 3.]
 [4. 0. 3. 2. 2.]
 [4. 0. 4. 1. 0.]]

See how I get an invalid individual? Note the repeated integers in row 1. The individual should look like this:

[[1.0 0.0 2.0 4.0 3.0]
 [4.0 3.0 0.0 1.0 2.0]
 [4.0 2.0 0.0 1.0 3.0]
 [4.0 0.0 3.0 2.0 1.0]
 [3.0 2.0 4.0 1.0 0.0]]

I hope this update makes it easier to understand what I am trying to do. Thanks for all the help so far! <3

EDIT 2

I was able to work around by converting everything to list and then with for loops substitute the values in place, but this should be super slow. There might be a way to do this using numpy.

maskedChildAFill = maskedChildAFill.tolist()
maskedChildBFill = maskedChildBFill.tolist()

maskedChildAFixed = maskedChildAFixed.tolist()
maskedChildBFixed = maskedChildBFixed.tolist()

for i in range(numMachines):
    counterA = 0
    counterB = 0

    for n, j in enumerate(maskedChildAFixed[i]):
        if maskedChildAFixed[i][n] is None:
            maskedChildAFixed[i][n] = maskedChildBFill[i][counterA]
            counterA += 1

    for n, j in enumerate(maskedChildBFixed[i]):
        if maskedChildBFixed[i][n] is None:
            maskedChildBFixed[i][n] = maskedChildAFill[i][counterB]
            counterB += 1
4
  • When reading code like this, I prefer to see some, if not all, the arrays. If I don't see them, I either have to run the code in my head and imagine the results, or copy-n-paste to a Ipython session and display them. Look at some good answers to see what I'm talking about. Commented Jun 12, 2020 at 4:05
  • Does this answer your question? Substituting values in a numpy masked array Commented Jun 12, 2020 at 5:14
  • @hpaulj I did update the question with more information and iPython outputs. I'm new around here, thanks for your suggestion. Commented Jun 12, 2020 at 10:01
  • @Joe It doesn't unfortunately, not specifically on this case. I have read this question before. Thanks for your help. Commented Jun 12, 2020 at 10:01

1 Answer 1

0

I think you are looking for this:

parentA = np.array(population[0])
parentB = np.array(population[1])

childA = np.zeros((numJobs, numMachines))
np.copyto(childA, parentA)

childB = np.zeros((numJobs, numMachines))
np.copyto(childB, parentB)

subJobs = np.stack([rng.choice(numJobs ,size=int(np.max([2, np.floor(numJobs/2)])), replace=False) for i in range(numMachines)])

maskA = np.stack([(np.isin(childA[i], subJobs[i])) for i in range(numMachines)])
invMaskA = np.invert(maskA)

maskB = np.stack([(np.isin(childB[i], subJobs[i])) for i in range(numMachines)])
invMaskB = np.invert(maskB)

maskedChildAFixed = np.ma.masked_array(childA, maskA)
maskedChildBFixed = np.ma.masked_array(childB, maskB)

maskedChildAFill = np.ma.masked_array(childB, invMaskA)
maskedChildBFill = np.ma.masked_array(childA, invMaskB)

from operator import and_
crossA = np.ma.array(maskedChildAFixed.filled(0)+maskedChildAFill.filled(0),mask=list(map(and_,maskedChildAFixed.mask,maskedChildAFill.mask)))
crossB = np.ma.array(maskedChildBFixed.filled(0)+maskedChildBFill.filled(0),mask=list(map(and_,maskedChildBFixed.mask,maskedChildBFill.mask)))

Please note that I change line maskedChildAFill = np.ma.masked_array(childB, invMaskA) to fit the description of your problem. If that is not what you want, simply change it back to your original code. The last two lines should do the work for you.

output:

crossA
[[4.0 0.0 2.0 1.0 4.0]
 [4.0 2.0 0.0 2.0 0.0]
 [2.0 2.0 3.0 1.0 3.0]
 [4.0 3.0 3.0 2.0 2.0]
 [2.0 0.0 4.0 1.0 0.0]]
crossB
[[2.0 0.0 1.0 1.0 4.0]
 [4.0 2.0 0.0 2.0 0.0]
 [2.0 2.0 3.0 1.0 1.0]
 [4.0 3.0 3.0 2.0 2.0]
 [4.0 0.0 4.0 1.0 2.0]]

EDIT: Per OP's edit on question, this would work for the purpose:

maskedChildAFixed[np.where(maskA)] = maskedChildBFill.ravel()
maskedChildBFixed[np.where(maskB)] = maskedChildAFill.ravel()

Example output for maskedChildAFixed:

[[4.0 0.0 2.0 1.0 3.0]
 [4.0 2.0 0.0 1.0 3.0]
 [3.0 2.0 0.0 1.0 4.0]
 [4.0 0.0 3.0 2.0 1.0]
 [1.0 3.0 4.0 2.0 0.0]]
Sign up to request clarification or add additional context in comments.

2 Comments

Hi Ehsan, thanks for the help. I have updated the question with a better code and explanation of what I am trying to do. The children that you produced are invalid, no integer should be repeated in each row.
@RafaelLucas Please find my edit on the post and let me know if it is what you needed. Thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.