Skip to content

Feature: Wall clock timers for RunWhile, Relax, Minimize#388

Open
jplauzie wants to merge 3 commits intomumax:3.11.2from
jplauzie:WallClock
Open

Feature: Wall clock timers for RunWhile, Relax, Minimize#388
jplauzie wants to merge 3 commits intomumax:3.11.2from
jplauzie:WallClock

Conversation

@jplauzie
Copy link

Adds a function that allows the user to add some noise, then Run/Relax/Minimize for a specified amount of wallclock time or (for Relax/Minimize) if the solver converges. This is useful for stiff problems where one particular solver gets stuck (In my experience, this can happen with e.g. hysteresis loops at small external fields), to help jiggle out of a local minimum.

  • noise, Runwhile, Relax, Minimize , are each consecutively called for some user-input wallclock time. If a user wants a different order (say, no Run), they can just input 0 walltime, judiciously. This can also be looped in a while(for) loop to keep cycling between run->relax->minimize until it converges.
  • The current implementation is a bit messy, I basically copied Minimize/Relax verbatim. It would be cleaner to integrate the inputs/flags into existing Relax/Minimize directly (or possibly using injections?) but I didn't want to mess with such sensitive core functions.
  • Introduces a new magnetization Config, CurrentMag. This just makes the current magnetization into a Config. This is mostly useful for adding noise, as "m=Currentmag().add(0.1,randommag())". Works with stuff like .transl() as one would expect. Can be a bit memory intensive as it stores a Hostcopy of the magnetization whereas Configs usually are a mathematical function. I have not seen any issues with e.g. memory leaks.

JonathanMaes and others added 3 commits March 12, 2026 15:12
@JonathanMaes JonathanMaes changed the base branch from master to 3.11.2 March 18, 2026 15:26
DeclFunc("RunSequence", RunSequence, " (total wallclock time, noise, wallclock1, wallclock 2, wallclock3, seed). Runs a sequence of steps: 1) Add noise to current magnetization as CurrentMag().Add(noise, RandomMagSeed(seed)), 2) Run for wallclock1 seconds, 3) Relax for wallclock2 seconds, 4) Minimize for wallclock3 seconds. The sequence is repeated until convergence or total wall-clock time limit is reached.")
}

func MinimizeForSeconds(seconds int) (converged bool) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function looks very similar to engine.Minimize(), apart from different comments and the 3 places where time is taken into account, so I think it would be beneficial to combine them to avoid duplicate code.

The original engine.Minimize() could, for instance, be replaced by a call to MinimizeForSeconds((1<<63 - 1) / int(time.Second)) (an integer "infinity"), while engine.MinimizeForSeconds(seconds int) could then be put in engine/minimizer.go.
I don't think there's much reason to consider this an "extension" (ext_), since not much unintuitive behavior can occur from this apart from possible unintended side effects from interrupting the minimizer mid-minimization.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there no cleaner solution than "integer infinity"? perhaps by making the seconds optional (my Go is not good enough to know if this is possible)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sadly, Go has no optional arguments. I think this is the one thing that annoys me most in Go.
A cleaner solution would be to use float infinity, but that has the issue of making time.Duration overflow to a negative duration, so a check should then be added to consider a negative seconds to be equivalent to infinity.

)

func init() {
DeclFunc("MinimizeForSeconds", MinimizeForSeconds, "Minimize for a fixed wall-clock time (int seconds)")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a small warning could be added that interrupting the minimizer before convergence does not guarantee correct solutions. It would also be useful to tell the user that the boolean return value indicates whether convergence was reached.


func init() {
DeclFunc("MinimizeForSeconds", MinimizeForSeconds, "Minimize for a fixed wall-clock time (int seconds)")
DeclFunc("RelaxForSeconds", RelaxForSeconds, "Relax for a fixed wall-clock time (int seconds)")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment, except that interrupting relax() should be fine at any point in time so no warning is needed here.

DeclFunc("RelaxForSeconds", RelaxForSeconds, "Relax for a fixed wall-clock time (int seconds)")
DeclFunc("RunForSeconds", RunForSeconds, "Run the simulation for a fixed wall-clock time (int seconds)")
DeclFunc("CurrentMag", CurrentMag, "Returns the current magnetization as a Config. E.g. CurrentMag().Add(0.1, RandomMagSeed(123)) will return a Config with the current magnetization plus some noise.")
DeclFunc("RunSequence", RunSequence, " (total wallclock time, noise, wallclock1, wallclock 2, wallclock3, seed). Runs a sequence of steps: 1) Add noise to current magnetization as CurrentMag().Add(noise, RandomMagSeed(seed)), 2) Run for wallclock1 seconds, 3) Relax for wallclock2 seconds, 4) Minimize for wallclock3 seconds. The sequence is repeated until convergence or total wall-clock time limit is reached.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this function might be too specialized to include in the standard mumax³ library. It seems like this is something users can just implement in an .mx3 script if they would need it, unless I'm missing something here?

DeclFunc("MinimizeForSeconds", MinimizeForSeconds, "Minimize for a fixed wall-clock time (int seconds)")
DeclFunc("RelaxForSeconds", RelaxForSeconds, "Relax for a fixed wall-clock time (int seconds)")
DeclFunc("RunForSeconds", RunForSeconds, "Run the simulation for a fixed wall-clock time (int seconds)")
DeclFunc("CurrentMag", CurrentMag, "Returns the current magnetization as a Config. E.g. CurrentMag().Add(0.1, RandomMagSeed(123)) will return a Config with the current magnetization plus some noise.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a very useful function (it could already come in handy in #391), but I would put it in engine/config.go.

DeclFunc("RunSequence", RunSequence, " (total wallclock time, noise, wallclock1, wallclock 2, wallclock3, seed). Runs a sequence of steps: 1) Add noise to current magnetization as CurrentMag().Add(noise, RandomMagSeed(seed)), 2) Run for wallclock1 seconds, 3) Relax for wallclock2 seconds, 4) Minimize for wallclock3 seconds. The sequence is repeated until convergence or total wall-clock time limit is reached.")
}

func MinimizeForSeconds(seconds int) (converged bool) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like the time.Duration doesn't play nicely with floats, but I wonder if it would be worth making seconds a floating-point number. Though on the other hand, an integer amount of seconds is probably already sufficiently flexible.

return converged
}

func RunForSeconds(seconds int) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems perfectly fine, can go below Run() in engine/run.go.

return converged
}

func RelaxForSeconds(seconds int) (converged bool) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comment but for engine.Relax() in engine/relax.go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants