Studio Matrx Monthly · Volume 1 · Issue 3 · August 2026
Amogh N P
 In loving memory of Amogh N P — Architect · Designer · Visionary 
Optimization and Simulation LoopsLesson 9.4
PSD for Architecture, Planning & Urban Design/Module 9 · Generative & Parametric Scripting

Lesson 9.4 · Generative & Parametric Scripting

Optimization and Simulation Loops

The generate-evaluate-improve loop - fitness functions, hill climbing, genetic algorithms and Galapagos - that lets code search for good designs

14 min Interactive lessonFree · open lessonByAmogh N P· Architect & interior designer
The hook

Generating a thousand designs is easy. The real leverage is teaching code to tell the good ones from the bad - and search toward the best.

The previous lessons all generate form. But generation alone floods you with options; the harder, more valuable question is which option is best, and can the computer help you find it rather than just make more.

It can - if you can score a design with a number. Once a fitness function turns 'good' into a value, you can run a loop that generates candidates, evaluates them, and keeps improving. This is optimization, and it is how tools like Grasshopper's Galapagos search vast option spaces. This lesson builds the loop from scratch and is honest about its limits.

Search is the computer's job. Defining 'good' and judging results are yours.

The generate-evaluate-improve loop

Every optimization, from a two-line hill climber to an industrial solver, is the same loop: generate a candidate design, evaluate it with a score, and improve by keeping what scores well and trying variations of it - then repeat until it is good enough. It is the generate-and-test idea from the packing lesson, now aimed deliberately at getting better rather than merely valid.

The loop only works if two things are in place. First, a design you can describe with numbers - parameters like a window ratio, a wall thickness, a column spacing. Second, a way to turn any set of those numbers into a single score. With those, the skeleton is tiny:

python
def optimize(evaluate, propose, start, steps=100):
    best = start
    best_score = evaluate(best)
    for _ in range(steps):
        candidate = propose(best)          # a nearby variation
        score = evaluate(candidate)
        if score < best_score:             # lower = better here
            best, best_score = candidate, score
    return best, best_score

That is the entire shape of optimization. Everything sophisticated - genetic algorithms, simulated annealing, gradient methods - is a cleverer propose (how you make the next candidate) or a smarter rule for what to keep. The designer's real work is the other two pieces: choosing the parameters and, above all, writing the evaluate function that captures what 'good' means.

It is worth seeing how directly this connects to everything earlier in the module. The generative techniques - random variation, L-systems, attractors, packing - are all ways to generate candidates; a fitness function is a way to evaluate them; and the loop is what ties generation and evaluation into a search. In other words, optimization does not replace the earlier lessons, it puts them to work with a purpose. Where the packing algorithm proposed positions and tested them only for 'does it fit', optimization proposes whole designs and tests them for 'is it better' - the same generate-and-test reflex, now pointed at quality rather than mere validity. Once you hold that framing, the whole module reads as one idea building toward this loop.

GENERATE - EVALUATE - IMPROVEGENERATEmake a candidateEVALUATEscore it: fitness()IMPROVEkeep the better onesscoreranktry againgood enough?stop - keep the bestlet the loop search; you set the goal and judge the result
Zoom
Every optimization is this loop: generate a candidate design, evaluate it with a fitness function, improve by keeping the better ones, and repeat until it is good enough. You set the goal and judge the result; the loop does the searching. Sophisticated methods only change how candidates are proposed.

Generate -> evaluate -> improve -> repeat. The whole of optimization is this loop.

The fitness function - turning 'good' into a number

The heart of optimization - and the part that is genuinely hard - is the fitness function (or objective function): the code that scores a design. Optimization can only chase what you can measure, so the entire exercise lives or dies on how honestly your score captures the real goal.

python
def fitness(width, height):
    area = width * height
    daylight_penalty = abs(area - 2.4)     # want ~2.4 sqm of glazing
    heat_penalty = 0.3 * area              # more glass, more heat gain
    return daylight_penalty + heat_penalty # lower is better

This toy window-sizing score balances two competing aims - enough daylight, but not so much glazing that heat gain runs away - and combines them into one number to minimise. That combining is the crux: real design goals conflict (cost versus quality, daylight versus heat, area versus circulation), and a single fitness function forces you to weigh them explicitly. Change the weight on heat_penalty and you get a different 'optimal' window - which is exactly right, because you have changed what you value.

Be deeply honest here: the optimizer is only as wise as the fitness function. It will exploit every loophole you leave - shrinking a window to nothing if you forgot to reward daylight - producing technically optimal, practically absurd results. Writing a fitness function that truly reflects a good building is a design skill, not a coding one, and it is where your judgement matters most.

When goals genuinely pull in different directions, collapsing them into one weighted number is not always the right move, because the weights hide the trade-off. The alternative is multi-objective optimization, which keeps the objectives separate and searches for the set of designs where you cannot improve one goal without harming another - the Pareto front. Instead of a single 'best' answer it hands you a spread of balanced options - this one cheaper, that one brighter, another cooler - and you choose where on the trade-off to sit. Grasshopper's Wallacei solver is built around exactly this, and it is often the more honest tool for real design, because it surfaces the compromise rather than burying it inside a weight you picked arbitrarily.

Optimizer chases only what you measure. Conflicting goals -> Pareto front, you choose.

Hill climbing, and why it gets stuck

The simplest optimizer is hill climbing: start somewhere, take a small random step, and keep it only if it improves the score - repeat. It literally walks uphill (or downhill, for a score you minimise) until it can improve no further.

python
import random

def cost(x):
    return (x - 3.6) ** 2 + 1.0          # lowest at x = 3.6

def hill_climb(start, step=0.4, iters=60):
    random.seed(0)
    x = start
    for _ in range(iters):
        candidate = x + random.uniform(-step, step)
        if cost(candidate) < cost(x):
            x = candidate
    return round(x, 3), round(cost(x), 3)

print(hill_climb(0.0))                   # closes in near x = 3.6

Hill climbing is easy and often good enough, but it has a famous flaw: it can get stuck on a local optimum - a peak that is higher than everything nearby but lower than the best peak elsewhere. Because it only ever steps to something immediately better, it cannot cross a valley to reach a taller hill it cannot see. On a simple landscape with one peak it finds the answer; on a rugged one it finds a good answer, not necessarily the best. The cures all add a way to escape local optima - random restarts, occasionally accepting a worse step (simulated annealing), or maintaining a whole population of candidates, which is where genetic algorithms come in.

CLIMBING A FITNESS LANDSCAPEfitnessdesign parameter ->local peakglobal peakHill climbing always steps uphill - so it can get stuck on a local peak and miss the best design.
Zoom
Optimization as climbing a fitness landscape. Hill climbing only ever steps uphill, so it reliably reaches the nearest peak - but it can get stuck on a local optimum and never cross the valley to the taller global peak. Escaping that trap is why genetic algorithms keep a whole population.

Hill climbing only steps uphill - so it can get stuck on a local peak.

Genetic algorithms and Galapagos

A genetic algorithm escapes the local-optimum trap by borrowing from evolution. Instead of one candidate, it keeps a whole population of designs. Each generation it scores them all, selects the fitter ones, crosses pairs to blend their parameters into offspring, and mutates a few values at random - then repeats. Because many candidates explore in parallel and crossover can jump across the landscape, a genetic algorithm is far better at escaping local optima than a lone hill climber. Here is the selection heart of one:

python
import random

random.seed(1)
def fitness(x):
    return -((x - 3.6) ** 2)             # higher is better

population = [random.uniform(0, 8) for _ in range(8)]
population.sort(key=fitness, reverse=True)
parents = population[:2]                 # select the fittest two
child = sum(parents) / 2                 # crossover: blend
child += random.uniform(-0.3, 0.3)       # mutation: nudge
print(round(child, 2))

A few dials govern how a genetic algorithm behaves, and they are worth naming because you will meet them in Galapagos too. Population size trades breadth for speed - a larger population explores more of the landscape but costs more evaluations per generation. The mutation rate balances exploration against stability - too low and the population stagnates, too high and it never settles. And elitism - always carrying the very best few designs unchanged into the next generation - stops evolution from accidentally throwing away its best result. You do not have to tune these expertly to get value; the defaults in real tools are sensible, and understanding what they mean is enough to read what the solver is doing.

In Grasshopper this is exactly what Galapagos (and solvers like Wallacei) do: you nominate sliders as genes and a single output as the fitness, press start, and it evolves the sliders toward the best score while you watch the population converge on the fitness graph. It is genuinely powerful for problems too large to search by hand - facade tuning, structural sizing, packing layouts. Be honest about the cost: evolutionary search is slow, needs many evaluations, and can still miss the true optimum. Treat its result as a strong, well-argued option to interrogate - not an oracle.

GA: population + select + crossover + mutate. Galapagos = this, in Grasshopper.

Simulation - closing the loop on performance

Optimization is only as meaningful as what the fitness function measures - and for real buildings that often means a simulation: code that predicts how a design performs before it exists. Daylight and glare, energy use, structural stress, solar gain, pedestrian flow, acoustics - each can be simulated, and the number that comes out becomes the fitness the optimizer chases. That closes the loop between form and performance: generate a form, simulate how it performs, improve toward better performance.

In practice you rarely write these simulators yourself - they are hard, specialised and validated over years. You call existing engines: Ladybug and Honeybee in Grasshopper (wrapping the Radiance and EnergyPlus engines) for daylight and energy, structural analysis tools for stress, computational-fluid-dynamics packages for airflow. Your Python or Grasshopper code sets up the design, hands it to the simulator, reads back the result, and feeds that into the optimization loop. Be realistic on two counts: simulations are approximations - only ever as trustworthy as their inputs and assumptions - and a full simulate-in-the-loop optimization can take hours or days, because each candidate must be fully simulated. Used well, though, this is the summit of the whole course: code that does not just draw or automate, but actively searches for better-performing designs - with you setting the goals and judging the results. From here, Module 10 turns to doing all of this well - debugging, version control and AI assistants.

Concepts & tools in this lesson

fitness function

Code that scores a design with one number

Also called the objective function. The optimizer chases only what this measures - so writing an honest one is the real design work.

hill climbing

Keep a small step only if it improves the score

Simplest optimizer; easy and often enough, but gets stuck on local optima. Cured by restarts, annealing or populations.

genetic algorithm

Evolve a population by selection, crossover and mutation

Escapes local optima via parallel search and crossover. Slower and evaluation-hungry, but strong on large, rugged option spaces.

Galapagos / Ladybug

Grasshopper evolutionary solver / simulation suite

Galapagos evolves sliders toward a fitness output; Ladybug and Honeybee simulate daylight and energy to supply that fitness.

Hands-on workshop

Workshop - optimize a window with a fitness function

Write a fitness function for a window that balances daylight against heat gain, then let a hill climber search for the best size - and deliberately break the fitness function to see the optimizer produce an absurd 'optimal' result.

Python 3 (random is built in). No external libraries required; the concepts transfer directly to Grasshopper Galapagos.

Given & goal
Goal: a working optimizer plus a feel for fitness-function pitfalls
Inputs: Python 3, the random module
Time: ~45 minutes
  1. 1Write fitness(width, height) that rewards glazing area near a daylight target but penalises total area for heat gain, returning one number to minimise (adapt the lesson's example).
  2. 2Write a hill climber that starts from a guess and, each step, nudges width and height by a small random amount, keeping the change only if fitness improves. Run it and print the best window it finds.
  3. 3Run the optimizer from several different starting points (and seeds). Do they all reach the same window, or different ones? Relate what you see to local optima.
  4. 4Now break it on purpose: remove the daylight reward, leaving only the heat penalty. Re-run and watch the optimizer shrink the window toward zero - a perfect illustration of 'the optimizer chases only what you measure'.
  5. 5Bonus: turn it into a tiny genetic algorithm - keep a population of eight windows, select the best two each generation, blend and mutate them - and compare how quickly it converges against the hill climber.

You’ll walk away with
A script with a fitness function and a hill climber that reports the best window size, notes from running it from multiple starts, and a one-line reflection on what happened when you removed the daylight reward.

The worked example

Three altitudes on the same idea

Read the band that fits you — or all three.

For the architectAutomate busywork & build custom tools

Performance-driven optimization is where computational design earns its keep on serious projects. Tuning a facade for daylight while limiting heat gain, sizing structure for minimum material, or orienting massing for solar performance are all fitness-function-plus-search problems that Galapagos, Wallacei and simulation tools like Ladybug/Honeybee handle at a scale no manual study can match. The discipline that matters is writing an objective that reflects a genuinely good building - not just the one number that is easy to measure.

For the interior designerScripts for data, schedules & layouts

Optimization thinking sharpens layout and specification decisions even without heavy engines. A fitness function can score a furniture layout for circulation and clearances, a lighting scheme for coverage against energy, or a material palette against a budget - letting you compare options against explicit, weighted criteria instead of by feel. The core skill transfers: decide what 'good' means as a measurable trade-off, and you argue design decisions far more convincingly.

For the studentA hireable computational skill

This is the frontier of the field and a standout portfolio topic. Building a hill climber, then a small genetic algorithm, and finally wiring a fitness function to a Grasshopper Galapagos run demonstrates exactly the performance-driven, optimization literacy that computational-design and building-performance roles hire for. It connects straight into the Academy's Computational Design and building-performance courses - and the honest understanding of local optima and fitness-function pitfalls will set your work apart.

Misconception check

Optimization finds the single best possible design automatically.

Two big caveats make that promise misleading. First, most practical optimizers - certainly hill climbing, and even genetic algorithms - find a very good design, not provably the best; they can get stuck on local optima and only search the space you defined. Second and more important, an optimizer maximises exactly the fitness function you wrote, nothing more. If that score does not capture what genuinely makes a good building - and real design goals conflict and resist being reduced to one number - the 'optimal' result can be technically perfect and practically useless. Optimization is a powerful search tool that finds strong options within the goals and parameters you set; it does not define what good means, and it does not replace the judgement that decides which of its answers to actually build.
Try it

Do it yourself

Reason about search and scoring.

  1. 1Name the three repeating stages of any optimization loop.
  2. 2What is a fitness function, and why is writing a good one a design skill rather than a coding one?
  3. 3Explain, in one sentence, why hill climbing can get stuck on a local optimum.
  4. 4How does a genetic algorithm's population help it escape a local optimum that traps a hill climber?
  5. 5What does Galapagos take as its 'genes' and its 'fitness' in Grasshopper?
Take this with you

The one line to carry out

Optimization is a loop - generate, evaluate, improve - that lets code search for good designs, but it can only chase what your fitness function measures. The search is the computer's job; defining 'good' and judging the result stay firmly yours.
Take it further
References & further reading

Peer-reviewed journals & authoritative standards

  1. 01Mathematical optimizationWikipedia, 2026.
  2. 02Generative designWikipedia, 2026.
  3. 03Grasshopper 3DWikipedia, 2026.
  4. 04AlgorithmWikipedia, 2026.
Related lessons
Recap
Optimization repeats a generate-evaluate-improve loop, and it needs two things: parameters that describe a design as numbers, and a fitness function that scores it. Hill climbing keeps improving steps but can get stuck on a local optimum; genetic algorithms keep a population and use selection, crossover and mutation to escape - which is what Grasshopper's Galapagos does with sliders and a fitness output. Simulation supplies real performance numbers to optimise, closing the loop between form and performance. The optimizer only ever chases what you measure.
Carry forward →

That completes the generative and parametric toolkit - randomness, recursion, form algorithms and optimization. Module 10 turns to doing all of it well: debugging, version control with git, coding with AI assistants, and building a computational-design career.

A

The author

Amogh N P

Architect, interior designer, and creative polymath. Studio Matrx began in his notebooks — his vision of design made honest, useful, and open to everyone. Its Academy is written and taught in his memory, and free, forever.

More about Amogh →