How I Learned to Stop Worrying and Love the LLM

25 June, 2025
Discuss on Zubax Forum >
maksim.drachov
Regular

Context: In order to magnetize/demagnetize the ferro-magnetic core, FluxGrip relies on a capacitor which is charged to a high voltage and then discharged through a coil (which is wrapped around the ferro-magnetic core). This cycle is defined by magnetization_params and demagnetization_params respectively.

For magnetization_params the values are [100, 100, 100] (with 100 being equal to the max voltage that can be atained by the capacitor). No tuning required here.

However, for demagnetization_params it’s not quite clear what these values should be. @pavel-kirienko had run some simulations, but said that there’s too many unknows/variables, and so it would be better to just “optimize empirically” (later). Initially, our demag sequence relied on the following simple formula:

# Exponential decay:
def sequence(decay: float):
   out = []
   for i in itertools.count():
       b = decay**i
       if round(b * 100) < 1:
           break
       s = -1 if i % 2 == 0 else +1
       out.append(s * b)
   return out
seq = sequence(0.9)
print("".join(f"{round(x * 100):+d}" for x in seq))

This results in the following series of values:

-100 +90 -81 +73 -66 +59 -53 +48 -43 +39 -35 +31 -28 +25 -23 +21 -19 +17 -15 +14 -12 +11 -10 +9 -8 +7 -6 +6 -5 +5 -4 +4 -3 +3 -3 +3 -2 +2 -2 +2 -1 +1 -1 +1 -1 +1 -1 +1 -1 +1 -1 

It looks right enough (+ means discharge in one direction, - in the other), and it works (kinda). After further manual tuning we obtained a result that was (somewhat) acceptable. The remaining magnetic force after a demagnetization cycle was around 5.5N and given that the payload is assumed to have some weight of itself, we figured this would suffice for most applications.

That is until the greatest military in the world (we’re not gonna say which) complained about the remaining magnetic force being “too large”. Shoot, now this has to be solved.

Step one was building a setup that would reliably measure the remaining magnetic force:

It works as follows:

  1. Using Cyphal/CAN the demagnetization values on FluxGrip are changed.
  2. The metal plate is placed on top of the magnet (it’s wrapped with 3d-printed standoffs to make sure it attaches properly)
  3. The magnet is magnetized and demagnetized.
  4. The stepper drive is turned on and it starts (slowly!) turning the metal rod which pulls the metal plate upwards.
  5. The strain gauges are used to measure the max force as the metal plate is pulled from the magnet. (It’s the metal bar on top of which the magnet is mounted, you can only see 1 in the picture, there’s a second one on the other side of the magnet.)

For one set of values, this process takes roughly one minute.

The force measurement rig was ready, it was time to start tuning! :smiling_face_with_sunglasses:


Someday…

Initially the plan was to use skopt.gp_minimize. I tried to be as helpful as possible: gave it a reasonable search space (range of values from -100 to +100, slowly descending to zero), a good starting point (which resulted in somewhere around 5N remaining magnetic force) and started the procedure. Unfortunately there were 2 issues with this approach:

  1. The optimization would sometimes select values for the demagnetization that would obviously not work (even though strictly speaking they did fall in the search space). Like, you don’t have to be Einstein to figure this is not what we’re looking for:
[100 0 100 0 98 0 100 0 100 ,,,,,,,]
  1. Second issue: too many parameters, the demagnetization sequence is quite a bit longer (50 values) and so even after letting the setup run for a whole night, no real improvement/convergence was found.

On Pavel’s suggestion I changed the approach slightly: instead of tuning 50 parameters, I would use a surrogate function that would generate these values from 4 parameters:


Above function generates 26 values but pretend it’s 50 (this was another dead-end where I tried to reduce the # of parameters by only tuning the tail end)

Anyway, this approach did not yield any result either. It would muck about, change values which more often than not would result in worse performance. I’ll be honest at this point I was starting to doubt whether it was possible to improve on the values we had at all?

On the 3th day of tuning I decided to apply some of my patented “vibe engineering” to the demagnetization sequence, eventually I managed to find a set of values that reduced the remaining magnetic force from 5N to 3N. Clearly, it is possible,… but the optimization algorithm lacks the intuition to tune this properly. This is when I sent a Slack message to Pavel:

I had nothing to lose, so let’s try this. I threw out gp_minimize and replaced it with the following:

And boy did it start tuning! After just 26 iterations (which is insanely fast) it managed to reduce the remaining force to 0.95N. Then my setup failed (to detect when the plate detached from the magnet, a \Delta{}F>0.5N needed to occur). So I decreased this delta to 0.2 N and re-started. As I’m writing this it has already managed to reduce the remaining force to 0.62 N (which required another measly 25 test cycles). The speed seems mainly due to the LLM selecting candidates that seem indeed sensible to test, as opposed to the algebraic algorithm which tries to test absolutely everything under the sun (except for the values that would intuitively make sense).

So there you have it: the singularity is near.

Discuss on Zubax Forum >