algorithmic modeling for Rhino
Hi, for my thesis I am looking into the subject of generative structural design. I am currently setting up a simple design problem to help me understand how to use the Wallacei component. The design problem is included as a figure.
- Objectives: Minimize weight & Minimize height
- Genes: Cross sections & Height
- Constraints: Maximum deflection (12 cm) & Maximum cross section utilization rate (0.8)
The script is setup accordingly (full definition included as attachment):
Without including the constraints the solver will quickly converge towards the lowest possible height (2m) and the lowest possible cross section (IPE120) to maximize weight reduction. I understand why it does this, as the two goals aren't naturally conflicting, but in fact they are conflicting when taking into account the constraints.
From an engineering point of view every solution which does not meet the constraints is ofcourse actually not a valid solution, I am looking for a way to tell the component that. I have attempted to solve the problem by adding a "constraints met?" objective to the solver. Here the number "1" is given when one or both constraints are not met and the number "0" is given when both constraints are met. As the solver tries to minimize the objectives, my reasoning was that it would work its way towards a set of solutions in which both constraints are met.
Unfortunatelly no result, the solver does not seem to bother with the "constraint" objective as can be seen in the screenshot of the results of the simulation.
Any help or thoughts would be greatly appreciated :) ! I have included the grasshopper script which uses the Karamba plugin and the wallacei plugin as attachment.
p.s. To avoid any comments advicing me to use the Karamaba "optimize cross section" component, it won't serve the purpose of my research.
Thank you for your question and a clear explanation of the problem.
There are multiple ways to address the “constraint” while using WallaceiX. I should say each of which has its own advantages and disadvantages and is dependent on the experiment set up and the complexity of the problem. I will explain them one by one in the following lines and hopefully one of them will help you to continue your research forward. Not all of them may apply to your case but it will give you an idea of how to tackle this problem and you may come up with another way of doing it.
1-The easiest and fastest way to force the generative process to produce solutions with “deflection” and “utilization rate” within the desired range is to constraint your genes (sliders) that are contributing to changes of these two values in a range to only create solutions with “deflection” and “utilization rate” in the domain. By looking at your grasshopper definition I can say that this may not be the best way to move forward because it is quite hard to find the right domain in your genes and sliders in the first look.
2-You can record the “deflection values” and “Utilization rate” for each individual in the entire simulation and then when simulation finished you can simply select the solutions in which these two values are within your desired range. You can record any numerical values for the entire simulation by connecting them to the Data input in WallaceiX. So, if you record these two constraints/values, when simulation finishes you can simply use the Data output of WallaceiX to choose the ones that are in the domain and use the same true/false pattern to cull/select the correct individuals. As you can see in the second image, the outputted data from all the outputs in WallaceiX have the same data structure. The exported phenotypes will also be in the branches in which the path digits are “Generation number” and “individual number”.
3-This one is a bit tricky. You can create a condition which evaluates whether these values are within the range or not and use this condition to replace one of your objectives with an empty value before connecting it to WallaceiX. But Why? If for any reason WallaceiX encounters a null or empty objective, it discards that individual behind the scene and creates another individual. Again, for the new individual, if this condition is not met and the objective again is replaced with an empty value, WallaceiX will be creating another individual. This will continue until the individual that is being created has those values in the domain (condition is met, and the objective is not replaced with an empty value). You can check the video for null indicator in the link (https://www.youtube.com/watch?v=rZcmr3CljtA&index=4&list=PL...)
Be careful this is a bit tricky way of doing it. In this way, we are actually trying to take advantage of null indicator (which has been created for another purpose) for another reason. WallaceiX may keep generating solutions with values not meeting your condition and will be stuck in a loop.
4-And the last one which is actually my favorite and I use it a lot. You can create a fitness objective to constrain these values in the desired domain and connect it to the objective input. I see you have already started doing this. but the way you created your objective is not the best practice. It should be as follows:
If you want to constrain a value in a range, you should add the absolute values of the subtraction of that value from the upper and lower end of that range. Then this number should be minimized. if there is no range and is just a single number, connect the absolute value of subtraction of “deflection value” from 12 (your desired number) and connect it to the fitness objective input. This means that simulation will try to reduce this value, it means that deflection values are getting closer and closer to 12 (or will be in a specified domain). Same way for the other value.
These are four ways that we usually deal with this condition, I hope it will be helpful. Not all of these solutions may be the best way of doing this in your case and you may actually find a better way of dealing with this problem. Please update us of your process and if you come up with a better solution share it with us too.
Thank you for your elaborate response! I would like to give my feedback on the obtained results using the four methods you described. Unfortunatally the conlcusion is that I have not yet found the answer to my problem.
1.Manually select the correct range for the genomes for which all solutions are valid
I was able to limit the range of my sliders and I will make a habit out of it to critically look at selecting a suitable range of genomes beforehand. Especially when problems become more complex I can image it reduces the run time of the simulation. However, you must agree that it does not tackle the core of the problem as some combinations of genomes will still result in design variants being created which do not meet the constraints.
Example (see figure): The range of “bottom chord and top chord” have been reduced by finding the minimum allowable when the other criteria (height in this case) is maximized. Following your suggestion to limit the range even further untill only valid outputs are created means I will actually lose some valid outputs (such as the one displayed in the top figure). In my opinion that completely defeats the purpose of applying generative design.
2.Record the “constraint” for each simulation
Great you mentioned this use of the data input of the component, I did not think of it! I will be using this to check if I have obtained valid solutions and filter out any non-valid solutions. Please see the setup I created for this “branch culling” below based on an example list (if you have a simpler way of doing this please let me know)
Notice that this method alone still does not provide a solution to the problem as the constraints are not included as objectives this way. Therefore WallaceiX will again converge towards minimizing the two goals without understanding the connection between them. It is however a great addition as final filtering step after the selection process!
3. Taking advantage of the null indicator
This has crossed my mind and I wanted to stay away from it as long as possible, but you mentioned it so I gave it a try. In theory it is however exactly the answer I am looking for. Throw out all solutions which do not meet the requirements and thus avoiding the algorithm from evolving based on “bad generations”. Here is the outcome of the attempt (after approx. 5 min of running):
As you allready suggested WallaceiX keeps stuck in a loop. Exactly why I don’t know. Could you maybe elaborate on this? I would say there are enough valid solutions for WallaceiX to work with (especially after the 1st method has limited the genome range).
4. Setting up the constraints as a range
First of all, let me please react on the use of the word “range” as it does compromise what I am looking for. I am not looking to constrain a value in a range, I am looking to not have that value exceed a certain limit. While this might sound similair in my opinion it is not. Please let me elaborte on this.
In theory yes, I complety agree with you that for my specific case getting closer to the maximum allowable value for deflection and utilization will optimize the problem as it indicates more efficient use of material. However, this does wrongfully tell WallaceiX that getting closer to the maximum value of the constaint (deflection and utilization in my case) is always better. What if I expand my problem and include objectives suh as ai mto reduce the number of different cross sections? Or reduce node complexity? Or cost reduction? These criteria do not nescassarely benefit from optimizing towards a range set out for maximum allowable deflection and stresses.
Basically what I am saying is, am I not wrongfully informing WallaceiX this way? I would really like your opinion on this matter, as I will try and include the above mentioned design goals at a later stage of my research.
The setup for now: combination of 1, 2, 4
I have used method 1, 2, and 4 in combination to redefine my script. In summary, compared tot he original setup what I did was:
After running several times, with adjustments to the constraint ranges and starting value of genomes, I was dissapointed to see that not one single solution (out of the 1250 population) was meeting both criteria as can bee seen in the screenshot below.
I have thoroughly checked the script and everything should work. Also when manually adjusting the genomes I am able to find valid solutions, so why can’t WallaceiX find these? What bothers me the most is that late generations still seem to deviate much from the optimum value for objective 3 and 4 (which are the constraints the solution should meet to actually be valid). Please see the figure below.
Some literature points out the use of weight factors as a method to assign more importance to certain objectives than others. Just a suggestion, but could this be a solution? If so, how would you suggest implementing this? I have allready attempted putting a x1000 multiplication factor on the constraint objectives to amplify the effect changes for these objectives. Again no valid solutions are found.
Ofcourse I have some personal interest in solving this problem, as well... my thesis sort of depends on solving it. But from an engineering point of view I think implementing constraints, boundary conditions, limits, (whatever you want to call it) is of great importance!
By sharing my results I hope to continue this discussion and work towards a solution with the gh commnity. I have attached the gh file as attachment. When running WallaceiX turn off the branch culling part of the script! It crashes Rhino sometimes when leaving it on.
I studied your model more in depth this time and I think there are a few notes worth pointing out in using evolutionary and multi-objective optimization. Utilizing an evolutionary algorithm in design is comprising of three important parts each of which holds equal weight in this process.
First and foremost is the formulation of the design problem. This means a) the algorithmic set up of the experiment, b) Genes (transformations and changes enabled to be explored) and c) fitness objectives (goals of which simulation is trying to achieve). No matter how powerful and advance an evolutionary engine would be, if the design experiment being investigated is not formulated (changes being enabled) properly, the engine won’t be able to output the optimal solutions. (we actually believe the design problem is even more important than anything else). Second is the engine itself and the third which we believe is very important as well is the analysis of the results precisely and statistically.
By studying your design problem one thing caught my eyes the most. The number of genes and transformations in your design problem is very limited. The only gene which can change and tweak the morphology of your truss is the height gene. Everything else which can essentially change the form is hardcoded. The rest of the genes in the experiment are just selecting a different combination of cross-sections. So essentially by looking at this setup, I can identify that the simulation can’t explore lots of solutions to address the objectives and constraints the way you want. And according to the following images simulation produced lots of repeated fitness values (in each fitness objective).
This following image is an example that shows lots of solutions are actually overlapping. The black lines in parallel coordinate plots are showing the solutions having the most repeated fitness objective 2 and as you can see black lines are all overlapped.
What I suggest at this stage is to go back to your design problem and try to reformulate it in a way to drive the simulation to explore the design space in a better way.
However, I ran your file for the population of 5000, Gen size 50 and Gen Count 100 and as you can see in the screenshot there are solutions in which both constraints are met. These solutions are spread over the entire population, from the first generations to the last.
About the weighting factors, We are working on it to implement it in the future releases.
Hope Wallacei would be helpful for you and good luck with your research and thesis.
Thank you once again for your detailed answer. Your explanation about correctly setting up an evolutionary design problem is much appreciated. I understand that my design space is very limited, however this was done intentionally as I was purely trying to prove the concept of evolutionary design on a very very simple test case.
The results you have obtained was all I wanted to achieve with this problem! However, I am not getting these results. No matter how large I set my population. I have tried to run the script for 1250, 5000, even 10000 population size. I also tried to run it on 2 different computers. Result: 0 valid outcomes...
The fact that you are obtaining valid results from the exact same script is great, but also very frustrating. I don't understand how you can run the exact same script and do manage to get valid results.
Nonetheless, I will mark the discussion as solved as the original question of this discussion has been answered, thank you for that!
I honestly wish I didn't allready try that as I am currently running out of options. I have been playing around with the population settings and algorithm parameters for the past 2 days on two different devices. Not a single valid result obtained so far.
Do you perhaps remember the exact WallaceiX setup you ran the simulation with?
I am currently redifining the design problem and script as suggested. I hope to obtain some valid results that way. Ofcourse it still bothers me knowing that I should be able to have WallaceiX create valid results, but somehow it won't when running the script on my computers.
I have found the component causing the error! The use of panels to input decimal numbers in the formulation of constraints completely messed up the results, causing the "2nd constraint" to never be reached.
Please see the figures and explanation below:
After manually adjusting the genomes (before running the simulation) nothing seems wrong with the use of panels to input a decimal number, as can be seen in the figure below:
After running the simulation however the the panel input of the decimal number is not recognized anymore and it is recognized as whole number. The intended value of 0.9 is now inputted as the value 9.
This clarifies why in my case I was only able to find solutions meeting 1 of the 2 constraints. The input "A" of the "<" component was originally also connected to a panel, causing the component to read 576605 instead of 5,7 in the case shown above. After replacing all panels with sliders I am now obtaining valid results from the simulation!
Again thank you for all the help!
Great. You got this.
for the future generations - grasshopper sometimes reads panel values as vectors and tries to recompute them to numbers so it outputs the vector lengths in this case.