Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Summary of issues with ODE Bayesian parameter estimation benchmarks #496

Open
nsiccha opened this issue Sep 6, 2022 · 18 comments
Open

Summary of issues with ODE Bayesian parameter estimation benchmarks #496

nsiccha opened this issue Sep 6, 2022 · 18 comments

Comments

@nsiccha
Copy link

nsiccha commented Sep 6, 2022

For now, I'd take the current ODE Bayesian Parameter estimation benchmarks offline and stop quoting them as up-to date evidence that Julia is currently 3-5 times faster than Stan for ODE problems. If I knew Julia better I'd fix them, but I don't, so here are the issues with them that I currently know:

  • The Lorenz benchmark fails to sample from the correct posterior for either library
  • The Lorenz benchmark uses 500+1000 warmup+samples for Turing, but 1000+1000 for Stan
  • The Lotka-Volterra benchmark samples from two different posteriors (parameters = sigma1.1, sigma1.2, theta_1, theta_2, theta_3, theta_4 for Stan and parameters = theta[1], theta[2], theta[3], theta[4], σ[1] for Turing)
  • Neither benchmark makes it clear what Stan's actual sampling time is, because the time reported through @Btime includes compilation
  • DynamicHMC only reports runtime and nothing else that is relevant
  • AFAIK, in the benchmarks Stan always uses adapt_delta=.85 (its default) while Turing uses "adapt_delta=.65", which potentially lowers runtime but also ESS
  • Runtime isn't a good metric anyways, I would probably display per parameter ESS/s histograms/boxplots over several runs with less (post warm-up) samples
  • The interesting problems with ODE models tend to only come up with non-toy problems, but they also tend to take much longer to run, making it difficult (I assume) to include them in automatic benchmarks
@nsiccha
Copy link
Author

nsiccha commented Sep 6, 2022

Also, any benchmark should probably include multiple chains run in parallel and combined to estimate mixing/ESS.

@ChrisRackauckas
Copy link
Member

We do not take benchmarks offline when someone does not like the results. That's not how open science works. They are open for everyone to edit. If you want to see a change, please edit them. As of right now, they are the best measurements we know of. If you don't like them, please submit a pull request. We have on-going pull requests such as #495. We do all of our development in the open for everyone to see: taking down the benchmarks is tantamount to stopping development.

The point of open benchmarks is to keep improving them, not to hide them. We don't do that when we look good, and we do not do that when we look bad. We share as much information as possible about the accuracy with the timings so that people can made educated statements about the results given what they see. Given the remarks you have been able to make just by reading the results, it's clear that we have shared enough information for people to see the good and the flaws of the current status of these benchmarks. That means that it's doing it's job sufficiently well of remarking on the current status, and if anything more should be added to further characterize the landscape, not less.

The Lorenz benchmark fails to sample from the correct posterior for either library

That sounds like good information to share online to everyone and to let people see if they can improve it. If there is no setup for these samplers to obtain this posterior for this relatively simple ODE, that's good information to have!

The Lorenz benchmark uses 500+1000 warmup+samples for Turing, but 1000+1000 for Stan

The Lotka-Volterra benchmark samples from two different posteriors (parameters = sigma1.1, sigma1.2, theta_1, theta_2, theta_3, theta_4 for Stan and parameters = theta[1], theta[2], theta[3], theta[4], σ[1] for Turing)

Thanks for pointing that out. Can you submit a PR?

Neither benchmark makes it clear what Stan's actual sampling time is, because the time reported through btime includes compilation

We looked at that in detail. There were changes made to omit compile time:

Even without that, it's referenced in there that the compile times are around 10 seconds. For a 1000 second benchmark, I do not think the 10 seconds are the biggest deal. It's also not a huge deal for Turing either. I'm not sure why there is such a focus on the 1%.

DynamicHMC only reports runtime and nothing else that is relevant

That should improve, yes. If you have suggestions, please open a PR.

Runtime isn't a good metric anyways, I would probably display per parameter ESS/s histograms/boxplots over several runs with less (post warm-up) samples

Let's do both. That would be most informative. How would I make such ESS/s histograms? Could you share the code, or open a PR?

AFAIK, in the benchmarks Stan always uses adapt_delta=.85 (its default) while Turing uses "adapt_delta=.65", which potentially lowers runtime but also ESS

Thanks for noting this. How would we change this in Stan? It might be easiest to just change Turing.jl and DynamicHMC.jl, but it would be nice to make this explicit and try multiple values. Maybe this could help the Lorenz results as well.

The interesting problems with ODE models tend to only come up with non-toy problems, but they also tend to take much longer to run, making it difficult (I assume) to include them in automatic benchmarks

The benchmark computers have 32-cores and routinely run benchmarks that take upwards of 16 hours. There are plenty of other PDE benchmarks included in this setup, for example there's https://benchmarks.sciml.ai/html/MOLPDE/Filament.html and then there's thousands of ODEs stiff equations https://benchmarks.sciml.ai/stable/Bio/BCR/ . There are big systems, and there are small systems. They use different methods. All matter in some way. If you only look at the ones on "toy" problems, then you will only see "toy" problems, but there are many more in this set if you click to the other pages.

That said, such "toy" problems are actually very similar to the PKPD models used for the estimation of dosing characteristics that was used for the dose predictions for the COVID-19 vaccine. As such, doing big repeated estimations on small sets of ODEs is not something to scoff at but rather is something that is a crucial part of many modern applications and should be optimized like any others (those PKPD estimations are routinely done other large datasets with many patients, so it may be <10 ODEs, but it's in some sense similar to doing tens of thousands of such estimations, so performance matters!)

@ChrisRackauckas
Copy link
Member

If I knew Julia better I'd fix them, but I don't, so here are the issues with them that I currently know:

that's fine. You can still contribute by sharing some Stan code. That's probably even more helpful since that's the part we know least. If you know how to make Stan output the ESS/s, please share.

@nsiccha
Copy link
Author

nsiccha commented Sep 6, 2022

You are of course free to do whatever you want, but I don't think it's reasonable to publish/keep online, link to and quote benchmarks which are known to have major and minor flaws and which tend to mislead non-experts. Even I had to rerun the code on my local machine and inspect the source code to understand what's going on, and I doubt many other people do that.

That being said, I don't know how to fix the benchmarks in Julia.

@ChrisRackauckas
Copy link
Member

These benchmarks are yours too. That's is why they are free to be edited. What we need more than anything is probably more Stan code, so if you have Stan code for the things you're asking for then please share.

@EvoArt
Copy link
Contributor

EvoArt commented Sep 6, 2022

Hopefully it should be straight forward to change adapt_delta in Stan. See bellow.
#258
#497

@ChrisRackauckas
Copy link
Member

With:

building in https://buildkite.com/julialang/scimlbenchmarks-dot-jl/builds/965, big chunks of this are handled. What I'm really missing are ways to get the ESS/s and do direct gradient timings. The Turing devs (@devmotion) can probably do the Turing side quite easily, though we might need some help on the Stan code.

@devmotion
Copy link
Member

For gradient comparisons one could use or build on https://github.com/torfjelde/TuringBenchmarking.jl (there was some discussion about moving it to TuringLang).

ess_rhat in MCMCChains already computes ESS/s if the computation time was tracked during sampling (which is the default in Turing): https://beta.turing.ml/MCMCChains.jl/dev/diagnostics/#MCMCDiagnosticTools.ess_rhat-Tuple{Chains} Since there is no completely standardized way to estimate ESS (MCMCDiagnosticTools includes three different algorithm which can yield quite different estimates sometimes, and they differ a bit from the algorithm in Stan (which we also want to add) which can yield yet another estimate), I think the fairest comparisons would be achieved by using exactly the same algorithm for all sampling algorithms. That could be achieved eg if StanSamples would track the computation time and save it in the MCMCChains.Chains object when constructing it.

@ChrisRackauckas
Copy link
Member

The Lotka-Volterra benchmark samples from two different posteriors (parameters = sigma1.1, sigma1.2, theta_1, theta_2, theta_3, theta_4 for Stan and parameters = theta[1], theta[2], theta[3], theta[4], σ[1] for Turing)

@devmotion can you help with sorting this one out? Once that's done, we can close this since all that's left is a duplicate of #494 if I haven't missed anything.

We should also make DynamicHMC print out more, though I'm not sure how to do that.

For gradient comparisons one could use or build on https://github.com/torfjelde/TuringBenchmarking.jl (there was some discussion about moving it to TuringLang).

Cool! That would be good to include.

@nsiccha
Copy link
Author

nsiccha commented Sep 7, 2022

I think the fairest comparisons would be achieved by using exactly the same algorithm for all sampling algorithms.

Yeah, that's what should be done.

What we need more than anything is probably more Stan code, so if you have Stan code for the things you're asking for then please share.

I think the Stan code/model is fine (where it defines the same posterior as the Turing code). I think what can be changed/improved is passing the right compile flags to Stan, but I wouldn't know how to do that from Julia.

Flags that seem to work and improve performance a bit (but not a lot) are:

STANCFLAGS+=--O1
STAN_CPP_OPTIMS=true
CXXFLAGS+= -O3

which should go into the cmdstan make/local. But no idea how this works using Julia and on the benchmarking machines 🤷

The other thing that should be improved is postprocessing/reporting of results, but as @devmotion explained, there are several options to estimates ESS, and I simply don't know the Julia ecosystem well enough to know which implementation to use or how to use it.

@devmotion
Copy link
Member

devmotion commented Sep 7, 2022

I think what can be changed/improved is passing the right compile flags to Stan.

IMO that would not make the benchmark fairer. I think the correct thing to do is to benchmark the Julia packages with the default compile flags (they are not optimized or tuned for the benchmarks) with Stan with the default compile flags. Of course, alternatively one could try to hyperoptimize everything as much as possible - but that's less realistic as users will just go with the default flags, I assume. If Stan's default compile flags are suboptimal they should be changed by Stan but I don't think it's fair to tune thrm for this benchmark.

but as @devmotion explained, there are several options to estimates ESS, and I simply don't know the Julia ecosystem well enough to know which implementation to use or how to use it.

As I wrote, since StanSample.jl already reports results in the same output format as Turing (and other packages) and this format already supports ESS/s, the only missing piece is that StanSample.jl has to include the computation time when constructing its output. Then estimates would be based on the same algorithm and comparisons would be fair.

@nsiccha
Copy link
Author

nsiccha commented Sep 7, 2022

IMO that would not make the benchmark fairer.

I disagree. In my experience, people fitting ODE models generally care about performance, and this includes changing some easily accessible compiler flags for Stan.

Similarly, you could just let Julia fit the model that "the average user" would code up, but this would be much slower?.
Similarly, disabling logging speeds fitting up considerably, should you do that for the benchmark or not?

Either of the choices for Julia will have a bigger impact on performance than enabling the "standard" Stan performance flags.

@EvoArt
Copy link
Contributor

EvoArt commented Sep 7, 2022

FWIW, I tend to agree with @nsiccha. Playing with compiler flags seems to be the norm for aggressively optimised Stan models. Since Turing models are being optimised by actual Turing devs, this doesn't seem unreasonable.

@devmotion
Copy link
Member

Surely the models (regardless of whether it's Stan or Turing or ...) should be optimized, and any help for optimizing the Stan models is very much appreciated (BTW I don't think the Turing models are completely optimized, definitely not if you're using DiffEqBayes - but that's a different topic). But there's no special compiler optimization or tricks going on in the Turing side - we just benchmark the package as it is and as users would install it. Similarly, maybe for Stan one should just install cmdstan via Conda? It seems that's what users on Windows, e.g., are supposed to do anyway: https://mc-stan.org/docs/cmdstan-guide/cmdstan-installation.html

@ChrisRackauckas
Copy link
Member

Our benchmark rules are: https://github.com/SciML/SciMLBenchmarks.jl#rules-optimal-fair-and-reproducible

These benchmarks are meant to represent good optimized coding style. Benchmarks are preferred to be run on the provided open benchmarking hardware for full reproducibility (though in some cases, such as with language barriers, this can be difficult). Each benchmark is documented with the compute devices used along with package versions for necessary reproduction. These benchmarks attempt to measure in terms of work-precision efficiency, either timing with an approximately matching the error or building work-precision diagrams for direct comparison of speed at given error tolerances.

So I think it's fair to use the better compiler flags. We try to setup everyone's "optimization tricks" wherever possible, but then also document their effects. So it would be good for example if we could have a version with special compiler flags and the default: that would demonstrate the amount of performance improvement due to the flags. It wouldn't be too hard to just have two Stan binaries built and swap the call?

We normally do this kind of gradation for everything. Stan is a bit of a PITA here since it's an order of magnitude harder to install than other packages, but now that we have a basis to work from it shouldn't be too bad.

BTW I don't think the Turing models are completely optimized, definitely not if you're using DiffEqBayes - but that's a different topic

They are definitely not, which is why I added the direct version so they can start being optimized. They are definitely not close to optimized yet though.

@nsiccha
Copy link
Author

nsiccha commented Sep 7, 2022

So it would be good for example if we could have a version with special compiler flags and the default

Yeah, I think it would also be a good idea to show Julia implementation which are optimized to different degrees, just so that people know what's possible and how big of an impact each step might have. But maybe this only encourages people to implent their model suboptimally...

@nsiccha
Copy link
Author

nsiccha commented Sep 7, 2022

I'm guessing it can also happen that some "optimizations" can even have an adverse effect in some cases, which would also be interesting to see.

@ChrisRackauckas
Copy link
Member

I'm guessing it can also happen that some "optimizations" can even have an adverse effect in some cases, which would also be interesting to see.

Indeed, like how in https://benchmarks.sciml.ai/dev/MultiLanguage/ode_wrapper_packages/#Stiff-Problem-2:-HIRES static arrays are worse than using in-place, even though the ODE is small. That's why we just try to show it all: the main use case of the benchmarks is just internal to help find performance regressions and figure out what the optimal way to do things is to change tutorials. For example, the current result of these Bayesian benchmarks has been to speedup the rate at which we deprecate the Turing wrapper, which is being made clear through the current results.

But maybe this only encourages people to implent their model suboptimally...

This is a completely separate repo from the docs and tutorials, so hopefully people aren't using it as a tutorial for anything other than how to choose methods

ChrisRackauckas added a commit that referenced this issue Sep 11, 2023
Since the objective function is now type-stable:

```julia
prob = build_opf_optimization_prob(dataset)
@code_warntype prob.f.f(test_u0, nothing)
```

```julia
MethodInstance for (::var"#opf_objective#497"{Dict{Int64, Vector{Float64}}, Dict{Int64, Int64}, Vector{Int64}})(::Vector{Float64}, ::Nothing)
  from (::var"#opf_objective#497")(x, param) @ Main c:\Users\accou\.julia\dev\SciMLBenchmarks\benchmarks\OptimizationFrameworks\optimal_powerflow.jmd:329
Arguments
  #self#::var"#opf_objective#497"{Dict{Int64, Vector{Float64}}, Dict{Int64, Int64}, Vector{Int64}}
  x::Vector{Float64}
  param::Core.Const(nothing)
Locals
  @_4::Union{Nothing, Tuple{Int64, Int64}}
  cost::Float64
  i::Int64
  _cost_arr::Vector{Float64}
  pg::Float64
Body::Float64
1 ─       (cost = 0.0)
│   %2  = Core.getfield(#self#, :ref_gen_idxs)::Vector{Int64}
│         (@_4 = Base.iterate(%2))
│   %4  = (@_4 === nothing)::Bool
│   %5  = Base.not_int(%4)::Bool
└──       goto #4 if not %5
2 ┄ %7  = @_4::Tuple{Int64, Int64}
│         (i = Core.getfield(%7, 1))
│   %9  = Core.getfield(%7, 2)::Int64
│   %10 = Core.getfield(#self#, :lookup_pg)::Dict{Int64, Int64}
│   %11 = Base.getindex(%10, i)::Int64
│         (pg = Base.getindex(x, %11))
│   %13 = Core.getfield(#self#, :cost_arrs)::Dict{Int64, Vector{Float64}}
│         (_cost_arr = Base.getindex(%13, i))
│   %15 = cost::Float64
│   %16 = Base.getindex(_cost_arr, 1)::Float64
│   %17 = Main.:^::Core.Const(^)
│   %18 = pg::Float64
│   %19 = Core.apply_type(Base.Val, 2)::Core.Const(Val{2})
│   %20 = (%19)()::Core.Const(Val{2}())
│   %21 = Base.literal_pow(%17, %18, %20)::Float64
│   %22 = (%16 * %21)::Float64
│   %23 = Base.getindex(_cost_arr, 2)::Float64
│   %24 = (%23 * pg)::Float64
│   %25 = Base.getindex(_cost_arr, 3)::Float64
│   %26 = (%22 + %24 + %25)::Float64
│         (cost = %15 + %26)
│         (@_4 = Base.iterate(%2, %9))
│   %29 = (@_4 === nothing)::Bool
│   %30 = Base.not_int(%29)::Bool
└──       goto #4 if not %30
3 ─       goto #2
4 ┄       return cost
```

Along with the constraint function

```julia
ret = zeros(length(prob.lcons))
@code_warntype prob.f.cons(ret, test_u0, nothing)
```

```julia
MethodInstance for (::var"#opf_constraints#498"{Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Tuple{Int64, Int64, Int64}, Int64}, Dict{Tuple{Int64, Int64, Int64}, Int64}, Vector{Tuple{Int64, Int64, Int64}}, Vector{Tuple{Int64, Int64, Int64}}, Dict{Int64, Vector{Tuple{Int64, Int64, Int64}}}, Dict{Int64, Vector{Int64}}, Vector{Int64}, Vector{Int64}, Dict{Int64, Int64}, Dict{Int64, Int64}, Dict{Int64, Int64}, Dict{Int64, Int64}, Dict{Int64, Int64}, Dict{Int64, Int64}})(::Vector{Float64}, ::Vector{Float64}, ::Nothing)
  from (::var"#opf_constraints#498")(ret, x, param) @ Main c:\Users\accou\.julia\dev\SciMLBenchmarks\benchmarks\OptimizationFrameworks\optimal_powerflow.jmd:341
Arguments
  #self#::var"#opf_constraints#498"{Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Int64, Float64}, Dict{Tuple{Int64, Int64, Int64}, Int64}, Dict{Tuple{Int64, Int64, Int64}, Int64}, Vector{Tuple{Int64, Int64, Int64}}, Vector{Tuple{Int64, Int64, Int64}}, Dict{Int64, Vector{Tuple{Int64, Int64, Int64}}}, Dict{Int64, Vector{Int64}}, Vector{Int64}, Vector{Int64}, Dict{Int64, Int64}, Dict{Int64, Int64}, Dict{Int64, Int64}, Dict{Int64, Int64}, Dict{Int64, Int64}, Dict{Int64, Int64}}
  ret::Vector{Float64}
  x::Vector{Float64}
  param::Core.Const(nothing)
Locals
  @_5::Union{Nothing, Tuple{Tuple{Int64, Tuple{Int64, Int64, Int64}}, Tuple{Int64, Int64}}}
  @_6::Union{Nothing, Tuple{Tuple{Int64, Tuple{Int64, Int64, Int64}}, Tuple{Int64, Int64}}}
  @_7::Union{Nothing, Tuple{Tuple{Int64, Tuple{Int64, Int64, Int64}}, Tuple{Int64, Int64}}}
  @_8::Union{Nothing, Tuple{Tuple{Int64, Tuple{Int64, Int64, Int64}}, Tuple{Int64, Int64}}}
  @_9::Union{Nothing, Tuple{Tuple{Int64, Tuple{Int64, Int64, Int64}}, Tuple{Int64, Int64}}}
  @_10::Union{Nothing, Tuple{Tuple{Int64, Tuple{Int64, Int64, Int64}}, Tuple{Int64, Int64}}}
  @_11::Union{Nothing, Tuple{Tuple{Int64, Tuple{Int64, Int64, Int64}}, Tuple{Int64, Int64}}}
  @_12::Union{Nothing, Tuple{Tuple{Int64, Int64}, Tuple{Int64, Int64}}}
  @_13::Union{Nothing, Tuple{Tuple{Int64, Int64}, Tuple{Int64, Int64}}}
  @_14::Union{Nothing, Tuple{Tuple{Int64, Int64}, Tuple{Int64, Int64}}}
  offsetidx::Int64
  @_16::Int64
  i@_17::Int64
  reti@_18::Int64
  #494::var"#494#500"{Vector{Float64}, Dict{Tuple{Int64, Int64, Int64}, Int64}}
  #493::var"#493#499"{Vector{Float64}, Dict{Int64, Int64}}
  @_21::Int64
  i@_22::Int64
  reti@_23::Int64
  #496::var"#496#502"{Vector{Float64}, Dict{Tuple{Int64, Int64, Int64}, Int64}}
  #495::var"#495#501"{Vector{Float64}, Dict{Int64, Int64}}
  @_26::Int64
  i@_27::Int64
  reti@_28::Int64
  @_29::Int64
  @_30::Int64
  j@_31::Int64
  l@_32::Int64
  i@_33::Int64
  reti@_34::Int64
  @_35::Int64
  @_36::Int64
  j@_37::Int64
  i@_38::Int64
  l@_39::Int64
  reti@_40::Int64
  @_41::Int64
  @_42::Int64
  j@_43::Int64
  i@_44::Int64
  l@_45::Int64
  reti@_46::Int64
  @_47::Int64
  @_48::Int64
  j@_49::Int64
  i@_50::Int64
  l@_51::Int64
  reti@_52::Int64
  @_53::Int64
  @_54::Int64
  j@_55::Int64
  i@_56::Int64
  l@_57::Int64
  reti@_58::Int64
  @_59::Int64
  @_60::Int64
  j@_61::Int64
  i@_62::Int64
  l@_63::Int64
  reti@_64::Int64
  @_65::Int64
  @_66::Int64
  j@_67::Int64
  i@_68::Int64
  l@_69::Int64
  reti@_70::Int64
Body::Nothing
1 ──        Core.NewvarNode(:(@_5))
│           Core.NewvarNode(:(@_6))
│           Core.NewvarNode(:(@_7))
│           Core.NewvarNode(:(@_8))
│           Core.NewvarNode(:(@_9))
│           Core.NewvarNode(:(@_10))
│           Core.NewvarNode(:(@_11))
│           Core.NewvarNode(:(@_12))
│           Core.NewvarNode(:(@_13))
│           (offsetidx = 0)
│    %11  = Core.getfield(#self#, :ref_buses_idxs)::Vector{Int64}
│    %12  = Main.enumerate(%11)::Base.Iterators.Enumerate{Vector{Int64}}
│           (@_14 = Base.iterate(%12))
│    %14  = (@_14 === nothing)::Bool
│    %15  = Base.not_int(%14)::Bool
└───        goto #4 if not %15
2 ┄─ %17  = @_14::Tuple{Tuple{Int64, Int64}, Tuple{Int64, Int64}}
│    %18  = Core.getfield(%17, 1)::Tuple{Int64, Int64}
│    %19  = Base.indexed_iterate(%18, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
│           (reti@_18 = Core.getfield(%19, 1))
│           (@_16 = Core.getfield(%19, 2))
│    %22  = Base.indexed_iterate(%18, 2, @_16::Core.Const(2))::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(3)])
│           (i@_17 = Core.getfield(%22, 1))
│    %24  = Core.getfield(%17, 2)::Tuple{Int64, Int64}
│    %25  = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %26  = Base.getindex(%25, i@_17)::Int64
│    %27  = Base.getindex(x, %26)::Float64
│    %28  = (reti@_18 + offsetidx::Core.Const(0))::Int64
│           Base.setindex!(ret, %27, %28)
│           (@_14 = Base.iterate(%12, %24))
│    %31  = (@_14 === nothing)::Bool
│    %32  = Base.not_int(%31)::Bool
└───        goto #4 if not %32
3 ──        goto #2
4 ┄─ %35  = offsetidx::Core.Const(0)
│    %36  = Core.getfield(#self#, :ref_buses_idxs)::Vector{Int64}
│    %37  = Main.length(%36)::Int64
│           (offsetidx = %35 + %37)
│    %39  = Core.getfield(#self#, :ref_bus_idxs)::Vector{Int64}
│    %40  = Main.enumerate(%39)::Base.Iterators.Enumerate{Vector{Int64}}
│           (@_13 = Base.iterate(%40))
│    %42  = (@_13 === nothing)::Bool
│    %43  = Base.not_int(%42)::Bool
└───        goto #7 if not %43
5 ┄─ %45  = @_13::Tuple{Tuple{Int64, Int64}, Tuple{Int64, Int64}}
│    %46  = Core.getfield(%45, 1)::Tuple{Int64, Int64}
│    %47  = Base.indexed_iterate(%46, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
│           (reti@_23 = Core.getfield(%47, 1))
│           (@_21 = Core.getfield(%47, 2))
│    %50  = Base.indexed_iterate(%46, 2, @_21::Core.Const(2))::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(3)])
│           (i@_22 = Core.getfield(%50, 1))
│    %52  = Core.getfield(%45, 2)::Tuple{Int64, Int64}
│    %53  = Main.:(var"#493#499")::Core.Const(var"#493#499")
│    %54  = Core.typeof(x)::Core.Const(Vector{Float64})
│    %55  = Core.getfield(#self#, :lookup_pg)::Dict{Int64, Int64}
│    %56  = Core.typeof(%55)::Core.Const(Dict{Int64, Int64})
│    %57  = Core.apply_type(%53, %54, %56)::Core.Const(var"#493#499"{Vector{Float64}, Dict{Int64, Int64}})
│    %58  = Core.getfield(#self#, :lookup_pg)::Dict{Int64, Int64}
│           (#493 = %new(%57, x, %58))
│    %60  = #493::var"#493#499"{Vector{Float64}, Dict{Int64, Int64}}
│    %61  = Core.getfield(#self#, :ref_bus_gens)::Dict{Int64, Vector{Int64}}
│    %62  = Base.getindex(%61, i@_22)::Vector{Int64}
│    %63  = Base.Generator(%60, %62)::Base.Generator{Vector{Int64}, var"#493#499"{Vector{Float64}, Dict{Int64, Int64}}}
│    %64  = (:init,)::Core.Const((:init,))
│    %65  = Core.apply_type(Core.NamedTuple, %64)::Core.Const(NamedTuple{(:init,)})
│    %66  = Core.tuple(0.0)::Core.Const((0.0,))
│    %67  = (%65)(%66)::Core.Const((init = 0.0,))
│    %68  = Core.kwcall(%67, Main.sum, %63)::Float64
│    %69  = Core.getfield(#self#, :bus_pd)::Dict{Int64, Float64}
│    %70  = Base.getindex(%69, i@_22)::Float64
│    %71  = (%68 - %70)::Float64
│    %72  = Core.getfield(#self#, :bus_gs)::Dict{Int64, Float64}
│    %73  = Base.getindex(%72, i@_22)::Float64
│    %74  = Main.:^::Core.Const(^)
│    %75  = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %76  = Base.getindex(%75, i@_22)::Int64
│    %77  = Base.getindex(x, %76)::Float64
│    %78  = Core.apply_type(Base.Val, 2)::Core.Const(Val{2})
│    %79  = (%78)()::Core.Const(Val{2}())
│    %80  = Base.literal_pow(%74, %77, %79)::Float64
│    %81  = (%73 * %80)::Float64
│    %82  = (%71 - %81)::Float64
│    %83  = Main.:(var"#494#500")::Core.Const(var"#494#500")
│    %84  = Core.typeof(x)::Core.Const(Vector{Float64})
│    %85  = Core.getfield(#self#, :p_idxmap)::Dict{Tuple{Int64, Int64, Int64}, Int64}
│    %86  = Core.typeof(%85)::Core.Const(Dict{Tuple{Int64, Int64, Int64}, Int64})
│    %87  = Core.apply_type(%83, %84, %86)::Core.Const(var"#494#500"{Vector{Float64}, Dict{Tuple{Int64, Int64, Int64}, Int64}})
│    %88  = Core.getfield(#self#, :p_idxmap)::Dict{Tuple{Int64, Int64, Int64}, Int64}
│           (#494 = %new(%87, x, %88))
│    %90  = #494::var"#494#500"{Vector{Float64}, Dict{Tuple{Int64, Int64, Int64}, Int64}}
│    %91  = Core.getfield(#self#, :ref_bus_arcs)::Dict{Int64, Vector{Tuple{Int64, Int64, Int64}}}
│    %92  = Base.getindex(%91, i@_22)::Vector{Tuple{Int64, Int64, Int64}}
│    %93  = Base.Generator(%90, %92)::Base.Generator{Vector{Tuple{Int64, Int64, Int64}}, var"#494#500"{Vector{Float64}, Dict{Tuple{Int64, Int64, Int64}, Int64}}}
│    %94  = Main.sum(%93)::Float64
│    %95  = (%82 - %94)::Float64
│    %96  = (reti@_23 + offsetidx)::Int64
│           Base.setindex!(ret, %95, %96)
│           (@_13 = Base.iterate(%40, %52))
│    %99  = (@_13 === nothing)::Bool
│    %100 = Base.not_int(%99)::Bool
└───        goto #7 if not %100
6 ──        goto #5
7 ┄─ %103 = offsetidx::Int64
│    %104 = Core.getfield(#self#, :ref_bus_idxs)::Vector{Int64}
│    %105 = Main.length(%104)::Int64
│           (offsetidx = %103 + %105)
│    %107 = Core.getfield(#self#, :ref_bus_idxs)::Vector{Int64}
│    %108 = Main.enumerate(%107)::Base.Iterators.Enumerate{Vector{Int64}}
│           (@_12 = Base.iterate(%108))
│    %110 = (@_12 === nothing)::Bool
│    %111 = Base.not_int(%110)::Bool
└───        goto #10 if not %111
8 ┄─ %113 = @_12::Tuple{Tuple{Int64, Int64}, Tuple{Int64, Int64}}
│    %114 = Core.getfield(%113, 1)::Tuple{Int64, Int64}
│    %115 = Base.indexed_iterate(%114, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
│           (reti@_28 = Core.getfield(%115, 1))
│           (@_26 = Core.getfield(%115, 2))
│    %118 = Base.indexed_iterate(%114, 2, @_26::Core.Const(2))::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(3)])
│           (i@_27 = Core.getfield(%118, 1))
│    %120 = Core.getfield(%113, 2)::Tuple{Int64, Int64}
│    %121 = Main.:(var"#495#501")::Core.Const(var"#495#501")
│    %122 = Core.typeof(x)::Core.Const(Vector{Float64})
│    %123 = Core.getfield(#self#, :lookup_qg)::Dict{Int64, Int64}
│    %124 = Core.typeof(%123)::Core.Const(Dict{Int64, Int64})
│    %125 = Core.apply_type(%121, %122, %124)::Core.Const(var"#495#501"{Vector{Float64}, Dict{Int64, Int64}})
│    %126 = Core.getfield(#self#, :lookup_qg)::Dict{Int64, Int64}
│           (#495 = %new(%125, x, %126))
│    %128 = #495::var"#495#501"{Vector{Float64}, Dict{Int64, Int64}}
│    %129 = Core.getfield(#self#, :ref_bus_gens)::Dict{Int64, Vector{Int64}}
│    %130 = Base.getindex(%129, i@_27)::Vector{Int64}
│    %131 = Base.Generator(%128, %130)::Base.Generator{Vector{Int64}, var"#495#501"{Vector{Float64}, Dict{Int64, Int64}}}
│    %132 = (:init,)::Core.Const((:init,))
│    %133 = Core.apply_type(Core.NamedTuple, %132)::Core.Const(NamedTuple{(:init,)})
│    %134 = Core.tuple(0.0)::Core.Const((0.0,))
│    %135 = (%133)(%134)::Core.Const((init = 0.0,))
│    %136 = Core.kwcall(%135, Main.sum, %131)::Float64
│    %137 = Core.getfield(#self#, :bus_qd)::Dict{Int64, Float64}
│    %138 = Base.getindex(%137, i@_27)::Float64
│    %139 = (%136 - %138)::Float64
│    %140 = Core.getfield(#self#, :bus_bs)::Dict{Int64, Float64}
│    %141 = Base.getindex(%140, i@_27)::Float64
│    %142 = Main.:^::Core.Const(^)
│    %143 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %144 = Base.getindex(%143, i@_27)::Int64
│    %145 = Base.getindex(x, %144)::Float64
│    %146 = Core.apply_type(Base.Val, 2)::Core.Const(Val{2})
│    %147 = (%146)()::Core.Const(Val{2}())
│    %148 = Base.literal_pow(%142, %145, %147)::Float64
│    %149 = (%141 * %148)::Float64
│    %150 = (%139 + %149)::Float64
│    %151 = Main.:(var"#496#502")::Core.Const(var"#496#502")
│    %152 = Core.typeof(x)::Core.Const(Vector{Float64})
│    %153 = Core.getfield(#self#, :q_idxmap)::Dict{Tuple{Int64, Int64, Int64}, Int64}
│    %154 = Core.typeof(%153)::Core.Const(Dict{Tuple{Int64, Int64, Int64}, Int64})
│    %155 = Core.apply_type(%151, %152, %154)::Core.Const(var"#496#502"{Vector{Float64}, Dict{Tuple{Int64, Int64, Int64}, Int64}})
│    %156 = Core.getfield(#self#, :q_idxmap)::Dict{Tuple{Int64, Int64, Int64}, Int64}
│           (#496 = %new(%155, x, %156))
│    %158 = #496::var"#496#502"{Vector{Float64}, Dict{Tuple{Int64, Int64, Int64}, Int64}}
│    %159 = Core.getfield(#self#, :ref_bus_arcs)::Dict{Int64, Vector{Tuple{Int64, Int64, Int64}}}
│    %160 = Base.getindex(%159, i@_27)::Vector{Tuple{Int64, Int64, Int64}}
│    %161 = Base.Generator(%158, %160)::Base.Generator{Vector{Tuple{Int64, Int64, Int64}}, var"#496#502"{Vector{Float64}, Dict{Tuple{Int64, Int64, Int64}, Int64}}}
│    %162 = Main.sum(%161)::Float64
│    %163 = (%150 - %162)::Float64
│    %164 = (reti@_28 + offsetidx)::Int64
│           Base.setindex!(ret, %163, %164)
│           (@_12 = Base.iterate(%108, %120))
│    %167 = (@_12 === nothing)::Bool
│    %168 = Base.not_int(%167)::Bool
└───        goto #10 if not %168
9 ──        goto #8
10 ┄ %171 = offsetidx::Int64
│    %172 = Core.getfield(#self#, :ref_bus_idxs)::Vector{Int64}
│    %173 = Main.length(%172)::Int64
│           (offsetidx = %171 + %173)
│    %175 = Core.getfield(#self#, :ref_arcs_from)::Vector{Tuple{Int64, Int64, Int64}}
│    %176 = Main.enumerate(%175)::Base.Iterators.Enumerate{Vector{Tuple{Int64, Int64, Int64}}}
│           (@_11 = Base.iterate(%176))
│    %178 = (@_11 === nothing)::Bool
│    %179 = Base.not_int(%178)::Bool
└───        goto #13 if not %179
11 ┄ %181 = @_11::Tuple{Tuple{Int64, Tuple{Int64, Int64, Int64}}, Tuple{Int64, Int64}}
│    %182 = Core.getfield(%181, 1)::Tuple{Int64, Tuple{Int64, Int64, Int64}}
│    %183 = Base.indexed_iterate(%182, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
│           (reti@_34 = Core.getfield(%183, 1))
│           (@_30 = Core.getfield(%183, 2))
│    %186 = Base.indexed_iterate(%182, 2, @_30::Core.Const(2))::Core.PartialStruct(Tuple{Tuple{Int64, Int64, Int64}, Int64}, Any[Tuple{Int64, Int64, Int64}, Core.Const(3)])
│    %187 = Core.getfield(%186, 1)::Tuple{Int64, Int64, Int64}
│    %188 = Base.indexed_iterate(%187, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
│           (l@_32 = Core.getfield(%188, 1))
│           (@_29 = Core.getfield(%188, 2))
│    %191 = Base.indexed_iterate(%187, 2, @_29::Core.Const(2))::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(3)])
│           (i@_33 = Core.getfield(%191, 1))
│           (@_29 = Core.getfield(%191, 2))
│    %194 = Base.indexed_iterate(%187, 3, @_29::Core.Const(3))::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(4)])
│           (j@_31 = Core.getfield(%194, 1))
│    %196 = Core.getfield(%181, 2)::Tuple{Int64, Int64}
│    %197 = Core.getfield(#self#, :br_g)::Dict{Int64, Float64}
│    %198 = Base.getindex(%197, l@_32)::Float64
│    %199 = Core.getfield(#self#, :br_g_fr)::Dict{Int64, Float64}
│    %200 = Base.getindex(%199, l@_32)::Float64
│    %201 = (%198 + %200)::Float64
│    %202 = Core.getfield(#self#, :br_ttm)::Dict{Int64, Float64}
│    %203 = Base.getindex(%202, l@_32)::Float64
│    %204 = (%201 / %203)::Float64
│    %205 = Main.:^::Core.Const(^)
│    %206 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %207 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %208 = Base.getindex(%207, l@_32)::Int64
│    %209 = Base.getindex(%206, %208)::Int64
│    %210 = Base.getindex(x, %209)::Float64
│    %211 = Core.apply_type(Base.Val, 2)::Core.Const(Val{2})
│    %212 = (%211)()::Core.Const(Val{2}())
│    %213 = Base.literal_pow(%205, %210, %212)::Float64
│    %214 = (%204 * %213)::Float64
│    %215 = Core.getfield(#self#, :br_g)::Dict{Int64, Float64}
│    %216 = Base.getindex(%215, l@_32)::Float64
│    %217 = -%216::Float64
│    %218 = Core.getfield(#self#, :br_tr)::Dict{Int64, Float64}
│    %219 = Base.getindex(%218, l@_32)::Float64
│    %220 = (%217 * %219)::Float64
│    %221 = Core.getfield(#self#, :br_b)::Dict{Int64, Float64}
│    %222 = Base.getindex(%221, l@_32)::Float64
│    %223 = Core.getfield(#self#, :br_ti)::Dict{Int64, Float64}
│    %224 = Base.getindex(%223, l@_32)::Float64
│    %225 = (%222 * %224)::Float64
│    %226 = (%220 + %225)::Float64
│    %227 = Core.getfield(#self#, :br_ttm)::Dict{Int64, Float64}
│    %228 = Base.getindex(%227, l@_32)::Float64
│    %229 = (%226 / %228)::Float64
│    %230 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %231 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %232 = Base.getindex(%231, l@_32)::Int64
│    %233 = Base.getindex(%230, %232)::Int64
│    %234 = Base.getindex(x, %233)::Float64
│    %235 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %236 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %237 = Base.getindex(%236, l@_32)::Int64
│    %238 = Base.getindex(%235, %237)::Int64
│    %239 = Base.getindex(x, %238)::Float64
│    %240 = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %241 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %242 = Base.getindex(%241, l@_32)::Int64
│    %243 = Base.getindex(%240, %242)::Int64
│    %244 = Base.getindex(x, %243)::Float64
│    %245 = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %246 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %247 = Base.getindex(%246, l@_32)::Int64
│    %248 = Base.getindex(%245, %247)::Int64
│    %249 = Base.getindex(x, %248)::Float64
│    %250 = (%244 - %249)::Float64
│    %251 = Main.cos(%250)::Float64
│    %252 = (%234 * %239 * %251)::Float64
│    %253 = (%229 * %252)::Float64
│    %254 = Core.getfield(#self#, :br_b)::Dict{Int64, Float64}
│    %255 = Base.getindex(%254, l@_32)::Float64
│    %256 = -%255::Float64
│    %257 = Core.getfield(#self#, :br_tr)::Dict{Int64, Float64}
│    %258 = Base.getindex(%257, l@_32)::Float64
│    %259 = (%256 * %258)::Float64
│    %260 = Core.getfield(#self#, :br_g)::Dict{Int64, Float64}
│    %261 = Base.getindex(%260, l@_32)::Float64
│    %262 = Core.getfield(#self#, :br_ti)::Dict{Int64, Float64}
│    %263 = Base.getindex(%262, l@_32)::Float64
│    %264 = (%261 * %263)::Float64
│    %265 = (%259 - %264)::Float64
│    %266 = Core.getfield(#self#, :br_ttm)::Dict{Int64, Float64}
│    %267 = Base.getindex(%266, l@_32)::Float64
│    %268 = (%265 / %267)::Float64
│    %269 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %270 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %271 = Base.getindex(%270, l@_32)::Int64
│    %272 = Base.getindex(%269, %271)::Int64
│    %273 = Base.getindex(x, %272)::Float64
│    %274 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %275 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %276 = Base.getindex(%275, l@_32)::Int64
│    %277 = Base.getindex(%274, %276)::Int64
│    %278 = Base.getindex(x, %277)::Float64
│    %279 = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %280 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %281 = Base.getindex(%280, l@_32)::Int64
│    %282 = Base.getindex(%279, %281)::Int64
│    %283 = Base.getindex(x, %282)::Float64
│    %284 = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %285 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %286 = Base.getindex(%285, l@_32)::Int64
│    %287 = Base.getindex(%284, %286)::Int64
│    %288 = Base.getindex(x, %287)::Float64
│    %289 = (%283 - %288)::Float64
│    %290 = Main.sin(%289)::Float64
│    %291 = (%273 * %278 * %290)::Float64
│    %292 = (%268 * %291)::Float64
│    %293 = (%214 + %253 + %292)::Float64
│    %294 = Core.getfield(#self#, :p_idxmap)::Dict{Tuple{Int64, Int64, Int64}, Int64}
│    %295 = Core.tuple(l@_32, i@_33, j@_31)::Tuple{Int64, Int64, Int64}
│    %296 = Base.getindex(%294, %295)::Int64
│    %297 = Base.getindex(x, %296)::Float64
│    %298 = (%293 - %297)::Float64
│    %299 = (reti@_34 + offsetidx)::Int64
│           Base.setindex!(ret, %298, %299)
│           (@_11 = Base.iterate(%176, %196))
│    %302 = (@_11 === nothing)::Bool
│    %303 = Base.not_int(%302)::Bool
└───        goto #13 if not %303
12 ─        goto #11
13 ┄ %306 = offsetidx::Int64
│    %307 = Core.getfield(#self#, :ref_arcs_from)::Vector{Tuple{Int64, Int64, Int64}}
│    %308 = Main.length(%307)::Int64
│           (offsetidx = %306 + %308)
│    %310 = Core.getfield(#self#, :ref_arcs_to)::Vector{Tuple{Int64, Int64, Int64}}
│    %311 = Main.enumerate(%310)::Base.Iterators.Enumerate{Vector{Tuple{Int64, Int64, Int64}}}
│           (@_10 = Base.iterate(%311))
│    %313 = (@_10 === nothing)::Bool
│    %314 = Base.not_int(%313)::Bool
└───        goto #16 if not %314
14 ┄ %316 = @_10::Tuple{Tuple{Int64, Tuple{Int64, Int64, Int64}}, Tuple{Int64, Int64}}
│    %317 = Core.getfield(%316, 1)::Tuple{Int64, Tuple{Int64, Int64, Int64}}
│    %318 = Base.indexed_iterate(%317, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
│           (reti@_40 = Core.getfield(%318, 1))
│           (@_36 = Core.getfield(%318, 2))
│    %321 = Base.indexed_iterate(%317, 2, @_36::Core.Const(2))::Core.PartialStruct(Tuple{Tuple{Int64, Int64, Int64}, Int64}, Any[Tuple{Int64, Int64, Int64}, Core.Const(3)])
│    %322 = Core.getfield(%321, 1)::Tuple{Int64, Int64, Int64}
│    %323 = Base.indexed_iterate(%322, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
│           (l@_39 = Core.getfield(%323, 1))
│           (@_35 = Core.getfield(%323, 2))
│    %326 = Base.indexed_iterate(%322, 2, @_35::Core.Const(2))::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(3)])
│           (i@_38 = Core.getfield(%326, 1))
│           (@_35 = Core.getfield(%326, 2))
│    %329 = Base.indexed_iterate(%322, 3, @_35::Core.Const(3))::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(4)])
│           (j@_37 = Core.getfield(%329, 1))
│    %331 = Core.getfield(%316, 2)::Tuple{Int64, Int64}
│    %332 = Core.getfield(#self#, :br_g)::Dict{Int64, Float64}
│    %333 = Base.getindex(%332, l@_39)::Float64
│    %334 = Core.getfield(#self#, :br_g_to)::Dict{Int64, Float64}
│    %335 = Base.getindex(%334, l@_39)::Float64
│    %336 = (%333 + %335)::Float64
│    %337 = Main.:^::Core.Const(^)
│    %338 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %339 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %340 = Base.getindex(%339, l@_39)::Int64
│    %341 = Base.getindex(%338, %340)::Int64
│    %342 = Base.getindex(x, %341)::Float64
│    %343 = Core.apply_type(Base.Val, 2)::Core.Const(Val{2})
│    %344 = (%343)()::Core.Const(Val{2}())
│    %345 = Base.literal_pow(%337, %342, %344)::Float64
│    %346 = (%336 * %345)::Float64
│    %347 = Core.getfield(#self#, :br_g)::Dict{Int64, Float64}
│    %348 = Base.getindex(%347, l@_39)::Float64
│    %349 = -%348::Float64
│    %350 = Core.getfield(#self#, :br_tr)::Dict{Int64, Float64}
│    %351 = Base.getindex(%350, l@_39)::Float64
│    %352 = (%349 * %351)::Float64
│    %353 = Core.getfield(#self#, :br_b)::Dict{Int64, Float64}
│    %354 = Base.getindex(%353, l@_39)::Float64
│    %355 = Core.getfield(#self#, :br_ti)::Dict{Int64, Float64}
│    %356 = Base.getindex(%355, l@_39)::Float64
│    %357 = (%354 * %356)::Float64
│    %358 = (%352 - %357)::Float64
│    %359 = Core.getfield(#self#, :br_ttm)::Dict{Int64, Float64}
│    %360 = Base.getindex(%359, l@_39)::Float64
│    %361 = (%358 / %360)::Float64
│    %362 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %363 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %364 = Base.getindex(%363, l@_39)::Int64
│    %365 = Base.getindex(%362, %364)::Int64
│    %366 = Base.getindex(x, %365)::Float64
│    %367 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %368 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %369 = Base.getindex(%368, l@_39)::Int64
│    %370 = Base.getindex(%367, %369)::Int64
│    %371 = Base.getindex(x, %370)::Float64
│    %372 = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %373 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %374 = Base.getindex(%373, l@_39)::Int64
│    %375 = Base.getindex(%372, %374)::Int64
│    %376 = Base.getindex(x, %375)::Float64
│    %377 = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %378 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %379 = Base.getindex(%378, l@_39)::Int64
│    %380 = Base.getindex(%377, %379)::Int64
│    %381 = Base.getindex(x, %380)::Float64
│    %382 = (%376 - %381)::Float64
│    %383 = Main.cos(%382)::Float64
│    %384 = (%366 * %371 * %383)::Float64
│    %385 = (%361 * %384)::Float64
│    %386 = Core.getfield(#self#, :br_b)::Dict{Int64, Float64}
│    %387 = Base.getindex(%386, l@_39)::Float64
│    %388 = -%387::Float64
│    %389 = Core.getfield(#self#, :br_tr)::Dict{Int64, Float64}
│    %390 = Base.getindex(%389, l@_39)::Float64
│    %391 = (%388 * %390)::Float64
│    %392 = Core.getfield(#self#, :br_g)::Dict{Int64, Float64}
│    %393 = Base.getindex(%392, l@_39)::Float64
│    %394 = Core.getfield(#self#, :br_ti)::Dict{Int64, Float64}
│    %395 = Base.getindex(%394, l@_39)::Float64
│    %396 = (%393 * %395)::Float64
│    %397 = (%391 + %396)::Float64
│    %398 = Core.getfield(#self#, :br_ttm)::Dict{Int64, Float64}
│    %399 = Base.getindex(%398, l@_39)::Float64
│    %400 = (%397 / %399)::Float64
│    %401 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %402 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %403 = Base.getindex(%402, l@_39)::Int64
│    %404 = Base.getindex(%401, %403)::Int64
│    %405 = Base.getindex(x, %404)::Float64
│    %406 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %407 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %408 = Base.getindex(%407, l@_39)::Int64
│    %409 = Base.getindex(%406, %408)::Int64
│    %410 = Base.getindex(x, %409)::Float64
│    %411 = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %412 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %413 = Base.getindex(%412, l@_39)::Int64
│    %414 = Base.getindex(%411, %413)::Int64
│    %415 = Base.getindex(x, %414)::Float64
│    %416 = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %417 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %418 = Base.getindex(%417, l@_39)::Int64
│    %419 = Base.getindex(%416, %418)::Int64
│    %420 = Base.getindex(x, %419)::Float64
│    %421 = (%415 - %420)::Float64
│    %422 = Main.sin(%421)::Float64
│    %423 = (%405 * %410 * %422)::Float64
│    %424 = (%400 * %423)::Float64
│    %425 = (%346 + %385 + %424)::Float64
│    %426 = Core.getfield(#self#, :p_idxmap)::Dict{Tuple{Int64, Int64, Int64}, Int64}
│    %427 = Core.tuple(l@_39, i@_38, j@_37)::Tuple{Int64, Int64, Int64}
│    %428 = Base.getindex(%426, %427)::Int64
│    %429 = Base.getindex(x, %428)::Float64
│    %430 = (%425 - %429)::Float64
│    %431 = (reti@_40 + offsetidx)::Int64
│           Base.setindex!(ret, %430, %431)
│           (@_10 = Base.iterate(%311, %331))
│    %434 = (@_10 === nothing)::Bool
│    %435 = Base.not_int(%434)::Bool
└───        goto #16 if not %435
15 ─        goto #14
16 ┄ %438 = offsetidx::Int64
│    %439 = Core.getfield(#self#, :ref_arcs_to)::Vector{Tuple{Int64, Int64, Int64}}
│    %440 = Main.length(%439)::Int64
│           (offsetidx = %438 + %440)
│    %442 = Core.getfield(#self#, :ref_arcs_from)::Vector{Tuple{Int64, Int64, Int64}}
│    %443 = Main.enumerate(%442)::Base.Iterators.Enumerate{Vector{Tuple{Int64, Int64, Int64}}}
│           (@_9 = Base.iterate(%443))
│    %445 = (@_9 === nothing)::Bool
│    %446 = Base.not_int(%445)::Bool
└───        goto #19 if not %446
17 ┄ %448 = @_9::Tuple{Tuple{Int64, Tuple{Int64, Int64, Int64}}, Tuple{Int64, Int64}}
│    %449 = Core.getfield(%448, 1)::Tuple{Int64, Tuple{Int64, Int64, Int64}}
│    %450 = Base.indexed_iterate(%449, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
│           (reti@_46 = Core.getfield(%450, 1))
│           (@_42 = Core.getfield(%450, 2))
│    %453 = Base.indexed_iterate(%449, 2, @_42::Core.Const(2))::Core.PartialStruct(Tuple{Tuple{Int64, Int64, Int64}, Int64}, Any[Tuple{Int64, Int64, Int64}, Core.Const(3)])
│    %454 = Core.getfield(%453, 1)::Tuple{Int64, Int64, Int64}
│    %455 = Base.indexed_iterate(%454, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
│           (l@_45 = Core.getfield(%455, 1))
│           (@_41 = Core.getfield(%455, 2))
│    %458 = Base.indexed_iterate(%454, 2, @_41::Core.Const(2))::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(3)])
│           (i@_44 = Core.getfield(%458, 1))
│           (@_41 = Core.getfield(%458, 2))
│    %461 = Base.indexed_iterate(%454, 3, @_41::Core.Const(3))::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(4)])
│           (j@_43 = Core.getfield(%461, 1))
│    %463 = Core.getfield(%448, 2)::Tuple{Int64, Int64}
│    %464 = Core.getfield(#self#, :br_b)::Dict{Int64, Float64}
│    %465 = Base.getindex(%464, l@_45)::Float64
│    %466 = Core.getfield(#self#, :br_b_fr)::Dict{Int64, Float64}
│    %467 = Base.getindex(%466, l@_45)::Float64
│    %468 = (%465 + %467)::Float64
│    %469 = -%468::Float64
│    %470 = Core.getfield(#self#, :br_ttm)::Dict{Int64, Float64}
│    %471 = Base.getindex(%470, l@_45)::Float64
│    %472 = (%469 / %471)::Float64
│    %473 = Main.:^::Core.Const(^)
│    %474 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %475 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %476 = Base.getindex(%475, l@_45)::Int64
│    %477 = Base.getindex(%474, %476)::Int64
│    %478 = Base.getindex(x, %477)::Float64
│    %479 = Core.apply_type(Base.Val, 2)::Core.Const(Val{2})
│    %480 = (%479)()::Core.Const(Val{2}())
│    %481 = Base.literal_pow(%473, %478, %480)::Float64
│    %482 = (%472 * %481)::Float64
│    %483 = Core.getfield(#self#, :br_b)::Dict{Int64, Float64}
│    %484 = Base.getindex(%483, l@_45)::Float64
│    %485 = -%484::Float64
│    %486 = Core.getfield(#self#, :br_tr)::Dict{Int64, Float64}
│    %487 = Base.getindex(%486, l@_45)::Float64
│    %488 = (%485 * %487)::Float64
│    %489 = Core.getfield(#self#, :br_g)::Dict{Int64, Float64}
│    %490 = Base.getindex(%489, l@_45)::Float64
│    %491 = Core.getfield(#self#, :br_ti)::Dict{Int64, Float64}
│    %492 = Base.getindex(%491, l@_45)::Float64
│    %493 = (%490 * %492)::Float64
│    %494 = (%488 - %493)::Float64
│    %495 = Core.getfield(#self#, :br_ttm)::Dict{Int64, Float64}
│    %496 = Base.getindex(%495, l@_45)::Float64
│    %497 = (%494 / %496)::Float64
│    %498 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %499 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %500 = Base.getindex(%499, l@_45)::Int64
│    %501 = Base.getindex(%498, %500)::Int64
│    %502 = Base.getindex(x, %501)::Float64
│    %503 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %504 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %505 = Base.getindex(%504, l@_45)::Int64
│    %506 = Base.getindex(%503, %505)::Int64
│    %507 = Base.getindex(x, %506)::Float64
│    %508 = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %509 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %510 = Base.getindex(%509, l@_45)::Int64
│    %511 = Base.getindex(%508, %510)::Int64
│    %512 = Base.getindex(x, %511)::Float64
│    %513 = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %514 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %515 = Base.getindex(%514, l@_45)::Int64
│    %516 = Base.getindex(%513, %515)::Int64
│    %517 = Base.getindex(x, %516)::Float64
│    %518 = (%512 - %517)::Float64
│    %519 = Main.cos(%518)::Float64
│    %520 = (%502 * %507 * %519)::Float64
│    %521 = (%497 * %520)::Float64
│    %522 = (%482 - %521)::Float64
│    %523 = Core.getfield(#self#, :br_g)::Dict{Int64, Float64}
│    %524 = Base.getindex(%523, l@_45)::Float64
│    %525 = -%524::Float64
│    %526 = Core.getfield(#self#, :br_tr)::Dict{Int64, Float64}
│    %527 = Base.getindex(%526, l@_45)::Float64
│    %528 = (%525 * %527)::Float64
│    %529 = Core.getfield(#self#, :br_b)::Dict{Int64, Float64}
│    %530 = Base.getindex(%529, l@_45)::Float64
│    %531 = Core.getfield(#self#, :br_ti)::Dict{Int64, Float64}
│    %532 = Base.getindex(%531, l@_45)::Float64
│    %533 = (%530 * %532)::Float64
│    %534 = (%528 + %533)::Float64
│    %535 = Core.getfield(#self#, :br_ttm)::Dict{Int64, Float64}
│    %536 = Base.getindex(%535, l@_45)::Float64
│    %537 = (%534 / %536)::Float64
│    %538 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %539 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %540 = Base.getindex(%539, l@_45)::Int64
│    %541 = Base.getindex(%538, %540)::Int64
│    %542 = Base.getindex(x, %541)::Float64
│    %543 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %544 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %545 = Base.getindex(%544, l@_45)::Int64
│    %546 = Base.getindex(%543, %545)::Int64
│    %547 = Base.getindex(x, %546)::Float64
│    %548 = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %549 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %550 = Base.getindex(%549, l@_45)::Int64
│    %551 = Base.getindex(%548, %550)::Int64
│    %552 = Base.getindex(x, %551)::Float64
│    %553 = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %554 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %555 = Base.getindex(%554, l@_45)::Int64
│    %556 = Base.getindex(%553, %555)::Int64
│    %557 = Base.getindex(x, %556)::Float64
│    %558 = (%552 - %557)::Float64
│    %559 = Main.sin(%558)::Float64
│    %560 = (%542 * %547 * %559)::Float64
│    %561 = (%537 * %560)::Float64
│    %562 = (%522 + %561)::Float64
│    %563 = Core.getfield(#self#, :q_idxmap)::Dict{Tuple{Int64, Int64, Int64}, Int64}
│    %564 = Core.tuple(l@_45, i@_44, j@_43)::Tuple{Int64, Int64, Int64}
│    %565 = Base.getindex(%563, %564)::Int64
│    %566 = Base.getindex(x, %565)::Float64
│    %567 = (%562 - %566)::Float64
│    %568 = (reti@_46 + offsetidx)::Int64
│           Base.setindex!(ret, %567, %568)
│           (@_9 = Base.iterate(%443, %463))
│    %571 = (@_9 === nothing)::Bool
│    %572 = Base.not_int(%571)::Bool
└───        goto #19 if not %572
18 ─        goto #17
19 ┄ %575 = offsetidx::Int64
│    %576 = Core.getfield(#self#, :ref_arcs_from)::Vector{Tuple{Int64, Int64, Int64}}
│    %577 = Main.length(%576)::Int64
│           (offsetidx = %575 + %577)
│    %579 = Core.getfield(#self#, :ref_arcs_to)::Vector{Tuple{Int64, Int64, Int64}}
│    %580 = Main.enumerate(%579)::Base.Iterators.Enumerate{Vector{Tuple{Int64, Int64, Int64}}}
│           (@_8 = Base.iterate(%580))
│    %582 = (@_8 === nothing)::Bool
│    %583 = Base.not_int(%582)::Bool
└───        goto #22 if not %583
20 ┄ %585 = @_8::Tuple{Tuple{Int64, Tuple{Int64, Int64, Int64}}, Tuple{Int64, Int64}}
│    %586 = Core.getfield(%585, 1)::Tuple{Int64, Tuple{Int64, Int64, Int64}}
│    %587 = Base.indexed_iterate(%586, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
│           (reti@_52 = Core.getfield(%587, 1))
│           (@_48 = Core.getfield(%587, 2))
│    %590 = Base.indexed_iterate(%586, 2, @_48::Core.Const(2))::Core.PartialStruct(Tuple{Tuple{Int64, Int64, Int64}, Int64}, Any[Tuple{Int64, Int64, Int64}, Core.Const(3)])
│    %591 = Core.getfield(%590, 1)::Tuple{Int64, Int64, Int64}
│    %592 = Base.indexed_iterate(%591, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
│           (l@_51 = Core.getfield(%592, 1))
│           (@_47 = Core.getfield(%592, 2))
│    %595 = Base.indexed_iterate(%591, 2, @_47::Core.Const(2))::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(3)])
│           (i@_50 = Core.getfield(%595, 1))
│           (@_47 = Core.getfield(%595, 2))
│    %598 = Base.indexed_iterate(%591, 3, @_47::Core.Const(3))::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(4)])
│           (j@_49 = Core.getfield(%598, 1))
│    %600 = Core.getfield(%585, 2)::Tuple{Int64, Int64}
│    %601 = Core.getfield(#self#, :br_b)::Dict{Int64, Float64}
│    %602 = Base.getindex(%601, l@_51)::Float64
│    %603 = Core.getfield(#self#, :br_b_to)::Dict{Int64, Float64}
│    %604 = Base.getindex(%603, l@_51)::Float64
│    %605 = (%602 + %604)::Float64
│    %606 = -%605::Float64
│    %607 = Main.:^::Core.Const(^)
│    %608 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %609 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %610 = Base.getindex(%609, l@_51)::Int64
│    %611 = Base.getindex(%608, %610)::Int64
│    %612 = Base.getindex(x, %611)::Float64
│    %613 = Core.apply_type(Base.Val, 2)::Core.Const(Val{2})
│    %614 = (%613)()::Core.Const(Val{2}())
│    %615 = Base.literal_pow(%607, %612, %614)::Float64
│    %616 = (%606 * %615)::Float64
│    %617 = Core.getfield(#self#, :br_b)::Dict{Int64, Float64}
│    %618 = Base.getindex(%617, l@_51)::Float64
│    %619 = -%618::Float64
│    %620 = Core.getfield(#self#, :br_tr)::Dict{Int64, Float64}
│    %621 = Base.getindex(%620, l@_51)::Float64
│    %622 = (%619 * %621)::Float64
│    %623 = Core.getfield(#self#, :br_g)::Dict{Int64, Float64}
│    %624 = Base.getindex(%623, l@_51)::Float64
│    %625 = Core.getfield(#self#, :br_ti)::Dict{Int64, Float64}
│    %626 = Base.getindex(%625, l@_51)::Float64
│    %627 = (%624 * %626)::Float64
│    %628 = (%622 + %627)::Float64
│    %629 = Core.getfield(#self#, :br_ttm)::Dict{Int64, Float64}
│    %630 = Base.getindex(%629, l@_51)::Float64
│    %631 = (%628 / %630)::Float64
│    %632 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %633 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %634 = Base.getindex(%633, l@_51)::Int64
│    %635 = Base.getindex(%632, %634)::Int64
│    %636 = Base.getindex(x, %635)::Float64
│    %637 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %638 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %639 = Base.getindex(%638, l@_51)::Int64
│    %640 = Base.getindex(%637, %639)::Int64
│    %641 = Base.getindex(x, %640)::Float64
│    %642 = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %643 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %644 = Base.getindex(%643, l@_51)::Int64
│    %645 = Base.getindex(%642, %644)::Int64
│    %646 = Base.getindex(x, %645)::Float64
│    %647 = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %648 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %649 = Base.getindex(%648, l@_51)::Int64
│    %650 = Base.getindex(%647, %649)::Int64
│    %651 = Base.getindex(x, %650)::Float64
│    %652 = (%646 - %651)::Float64
│    %653 = Main.cos(%652)::Float64
│    %654 = (%636 * %641 * %653)::Float64
│    %655 = (%631 * %654)::Float64
│    %656 = (%616 - %655)::Float64
│    %657 = Core.getfield(#self#, :br_g)::Dict{Int64, Float64}
│    %658 = Base.getindex(%657, l@_51)::Float64
│    %659 = -%658::Float64
│    %660 = Core.getfield(#self#, :br_tr)::Dict{Int64, Float64}
│    %661 = Base.getindex(%660, l@_51)::Float64
│    %662 = (%659 * %661)::Float64
│    %663 = Core.getfield(#self#, :br_b)::Dict{Int64, Float64}
│    %664 = Base.getindex(%663, l@_51)::Float64
│    %665 = Core.getfield(#self#, :br_ti)::Dict{Int64, Float64}
│    %666 = Base.getindex(%665, l@_51)::Float64
│    %667 = (%664 * %666)::Float64
│    %668 = (%662 - %667)::Float64
│    %669 = Core.getfield(#self#, :br_ttm)::Dict{Int64, Float64}
│    %670 = Base.getindex(%669, l@_51)::Float64
│    %671 = (%668 / %670)::Float64
│    %672 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %673 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %674 = Base.getindex(%673, l@_51)::Int64
│    %675 = Base.getindex(%672, %674)::Int64
│    %676 = Base.getindex(x, %675)::Float64
│    %677 = Core.getfield(#self#, :lookup_vm)::Dict{Int64, Int64}
│    %678 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %679 = Base.getindex(%678, l@_51)::Int64
│    %680 = Base.getindex(%677, %679)::Int64
│    %681 = Base.getindex(x, %680)::Float64
│    %682 = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %683 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %684 = Base.getindex(%683, l@_51)::Int64
│    %685 = Base.getindex(%682, %684)::Int64
│    %686 = Base.getindex(x, %685)::Float64
│    %687 = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %688 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %689 = Base.getindex(%688, l@_51)::Int64
│    %690 = Base.getindex(%687, %689)::Int64
│    %691 = Base.getindex(x, %690)::Float64
│    %692 = (%686 - %691)::Float64
│    %693 = Main.sin(%692)::Float64
│    %694 = (%676 * %681 * %693)::Float64
│    %695 = (%671 * %694)::Float64
│    %696 = (%656 + %695)::Float64
│    %697 = Core.getfield(#self#, :q_idxmap)::Dict{Tuple{Int64, Int64, Int64}, Int64}
│    %698 = Core.tuple(l@_51, i@_50, j@_49)::Tuple{Int64, Int64, Int64}
│    %699 = Base.getindex(%697, %698)::Int64
│    %700 = Base.getindex(x, %699)::Float64
│    %701 = (%696 - %700)::Float64
│    %702 = (reti@_52 + offsetidx)::Int64
│           Base.setindex!(ret, %701, %702)
│           (@_8 = Base.iterate(%580, %600))
│    %705 = (@_8 === nothing)::Bool
│    %706 = Base.not_int(%705)::Bool
└───        goto #22 if not %706
21 ─        goto #20
22 ┄ %709 = offsetidx::Int64
│    %710 = Core.getfield(#self#, :ref_arcs_to)::Vector{Tuple{Int64, Int64, Int64}}
│    %711 = Main.length(%710)::Int64
│           (offsetidx = %709 + %711)
│    %713 = Core.getfield(#self#, :ref_arcs_from)::Vector{Tuple{Int64, Int64, Int64}}
│    %714 = Main.enumerate(%713)::Base.Iterators.Enumerate{Vector{Tuple{Int64, Int64, Int64}}}
│           (@_7 = Base.iterate(%714))
│    %716 = (@_7 === nothing)::Bool
│    %717 = Base.not_int(%716)::Bool
└───        goto #25 if not %717
23 ┄ %719 = @_7::Tuple{Tuple{Int64, Tuple{Int64, Int64, Int64}}, Tuple{Int64, Int64}}
│    %720 = Core.getfield(%719, 1)::Tuple{Int64, Tuple{Int64, Int64, Int64}}
│    %721 = Base.indexed_iterate(%720, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
│           (reti@_58 = Core.getfield(%721, 1))
│           (@_54 = Core.getfield(%721, 2))
│    %724 = Base.indexed_iterate(%720, 2, @_54::Core.Const(2))::Core.PartialStruct(Tuple{Tuple{Int64, Int64, Int64}, Int64}, Any[Tuple{Int64, Int64, Int64}, Core.Const(3)])
│    %725 = Core.getfield(%724, 1)::Tuple{Int64, Int64, Int64}
│    %726 = Base.indexed_iterate(%725, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
│           (l@_57 = Core.getfield(%726, 1))
│           (@_53 = Core.getfield(%726, 2))
│    %729 = Base.indexed_iterate(%725, 2, @_53::Core.Const(2))::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(3)])
│           (i@_56 = Core.getfield(%729, 1))
│           (@_53 = Core.getfield(%729, 2))
│    %732 = Base.indexed_iterate(%725, 3, @_53::Core.Const(3))::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(4)])
│           (j@_55 = Core.getfield(%732, 1))
│    %734 = Core.getfield(%719, 2)::Tuple{Int64, Int64}
│    %735 = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %736 = Core.getfield(#self#, :f_bus)::Dict{Int64, Int64}
│    %737 = Base.getindex(%736, l@_57)::Int64
│    %738 = Base.getindex(%735, %737)::Int64
│    %739 = Base.getindex(x, %738)::Float64
│    %740 = Core.getfield(#self#, :lookup_va)::Dict{Int64, Int64}
│    %741 = Core.getfield(#self#, :t_bus)::Dict{Int64, Int64}
│    %742 = Base.getindex(%741, l@_57)::Int64
│    %743 = Base.getindex(%740, %742)::Int64
│    %744 = Base.getindex(x, %743)::Float64
│    %745 = (%739 - %744)::Float64
│    %746 = (reti@_58 + offsetidx)::Int64
│           Base.setindex!(ret, %745, %746)
│           (@_7 = Base.iterate(%714, %734))
│    %749 = (@_7 === nothing)::Bool
│    %750 = Base.not_int(%749)::Bool
└───        goto #25 if not %750
24 ─        goto #23
25 ┄ %753 = offsetidx::Int64
│    %754 = Core.getfield(#self#, :ref_arcs_from)::Vector{Tuple{Int64, Int64, Int64}}
│    %755 = Main.length(%754)::Int64
│           (offsetidx = %753 + %755)
│    %757 = Core.getfield(#self#, :ref_arcs_from)::Vector{Tuple{Int64, Int64, Int64}}
│    %758 = Main.enumerate(%757)::Base.Iterators.Enumerate{Vector{Tuple{Int64, Int64, Int64}}}
│           (@_6 = Base.iterate(%758))
│    %760 = (@_6 === nothing)::Bool
│    %761 = Base.not_int(%760)::Bool
└───        goto #28 if not %761
26 ┄ %763 = @_6::Tuple{Tuple{Int64, Tuple{Int64, Int64, Int64}}, Tuple{Int64, Int64}}
│    %764 = Core.getfield(%763, 1)::Tuple{Int64, Tuple{Int64, Int64, Int64}}
│    %765 = Base.indexed_iterate(%764, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
│           (reti@_64 = Core.getfield(%765, 1))
│           (@_60 = Core.getfield(%765, 2))
│    %768 = Base.indexed_iterate(%764, 2, @_60::Core.Const(2))::Core.PartialStruct(Tuple{Tuple{Int64, Int64, Int64}, Int64}, Any[Tuple{Int64, Int64, Int64}, Core.Const(3)])
│    %769 = Core.getfield(%768, 1)::Tuple{Int64, Int64, Int64}
│    %770 = Base.indexed_iterate(%769, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
│           (l@_63 = Core.getfield(%770, 1))
│           (@_59 = Core.getfield(%770, 2))
│    %773 = Base.indexed_iterate(%769, 2, @_59::Core.Const(2))::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(3)])
│           (i@_62 = Core.getfield(%773, 1))
│           (@_59 = Core.getfield(%773, 2))
│    %776 = Base.indexed_iterate(%769, 3, @_59::Core.Const(3))::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(4)])
│           (j@_61 = Core.getfield(%776, 1))
│    %778 = Core.getfield(%763, 2)::Tuple{Int64, Int64}
│    %779 = Main.:^::Core.Const(^)
│    %780 = Core.getfield(#self#, :p_idxmap)::Dict{Tuple{Int64, Int64, Int64}, Int64}
│    %781 = Core.tuple(l@_63, i@_62, j@_61)::Tuple{Int64, Int64, Int64}
│    %782 = Base.getindex(%780, %781)::Int64
│    %783 = Base.getindex(x, %782)::Float64
│    %784 = Core.apply_type(Base.Val, 2)::Core.Const(Val{2})
│    %785 = (%784)()::Core.Const(Val{2}())
│    %786 = Base.literal_pow(%779, %783, %785)::Float64
│    %787 = Main.:^::Core.Const(^)
│    %788 = Core.getfield(#self#, :q_idxmap)::Dict{Tuple{Int64, Int64, Int64}, Int64}
│    %789 = Core.tuple(l@_63, i@_62, j@_61)::Tuple{Int64, Int64, Int64}
│    %790 = Base.getindex(%788, %789)::Int64
│    %791 = Base.getindex(x, %790)::Float64
│    %792 = Core.apply_type(Base.Val, 2)::Core.Const(Val{2})
│    %793 = (%792)()::Core.Const(Val{2}())
│    %794 = Base.literal_pow(%787, %791, %793)::Float64
│    %795 = (%786 + %794)::Float64
│    %796 = (reti@_64 + offsetidx)::Int64
│           Base.setindex!(ret, %795, %796)
│           (@_6 = Base.iterate(%758, %778))
│    %799 = (@_6 === nothing)::Bool
│    %800 = Base.not_int(%799)::Bool
└───        goto #28 if not %800
27 ─        goto #26
28 ┄ %803 = offsetidx::Int64
│    %804 = Core.getfield(#self#, :ref_arcs_from)::Vector{Tuple{Int64, Int64, Int64}}
│    %805 = Main.length(%804)::Int64
│           (offsetidx = %803 + %805)
│    %807 = Core.getfield(#self#, :ref_arcs_to)::Vector{Tuple{Int64, Int64, Int64}}
│    %808 = Main.enumerate(%807)::Base.Iterators.Enumerate{Vector{Tuple{Int64, Int64, Int64}}}
│           (@_5 = Base.iterate(%808))
│    %810 = (@_5 === nothing)::Bool
│    %811 = Base.not_int(%810)::Bool
└───        goto #31 if not %811
29 ┄ %813 = @_5::Tuple{Tuple{Int64, Tuple{Int64, Int64, Int64}}, Tuple{Int64, Int64}}
│    %814 = Core.getfield(%813, 1)::Tuple{Int64, Tuple{Int64, Int64, Int64}}
│    %815 = Base.indexed_iterate(%814, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
│           (reti@_70 = Core.getfield(%815, 1))
│           (@_66 = Core.getfield(%815, 2))
│    %818 = Base.indexed_iterate(%814, 2, @_66::Core.Const(2))::Core.PartialStruct(Tuple{Tuple{Int64, Int64, Int64}, Int64}, Any[Tuple{Int64, Int64, Int64}, Core.Const(3)])
│    %819 = Core.getfield(%818, 1)::Tuple{Int64, Int64, Int64}
│    %820 = Base.indexed_iterate(%819, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
│           (l@_69 = Core.getfield(%820, 1))
│           (@_65 = Core.getfield(%820, 2))
│    %823 = Base.indexed_iterate(%819, 2, @_65::Core.Const(2))::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(3)])
│           (i@_68 = Core.getfield(%823, 1))
│           (@_65 = Core.getfield(%823, 2))
│    %826 = Base.indexed_iterate(%819, 3, @_65::Core.Const(3))::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(4)])
│           (j@_67 = Core.getfield(%826, 1))
│    %828 = Core.getfield(%813, 2)::Tuple{Int64, Int64}
│    %829 = Main.:^::Core.Const(^)
│    %830 = Core.getfield(#self#, :p_idxmap)::Dict{Tuple{Int64, Int64, Int64}, Int64}
│    %831 = Core.tuple(l@_69, i@_68, j@_67)::Tuple{Int64, Int64, Int64}
│    %832 = Base.getindex(%830, %831)::Int64
│    %833 = Base.getindex(x, %832)::Float64
│    %834 = Core.apply_type(Base.Val, 2)::Core.Const(Val{2})
│    %835 = (%834)()::Core.Const(Val{2}())
│    %836 = Base.literal_pow(%829, %833, %835)::Float64
│    %837 = Main.:^::Core.Const(^)
│    %838 = Core.getfield(#self#, :q_idxmap)::Dict{Tuple{Int64, Int64, Int64}, Int64}
│    %839 = Core.tuple(l@_69, i@_68, j@_67)::Tuple{Int64, Int64, Int64}
│    %840 = Base.getindex(%838, %839)::Int64
│    %841 = Base.getindex(x, %840)::Float64
│    %842 = Core.apply_type(Base.Val, 2)::Core.Const(Val{2})
│    %843 = (%842)()::Core.Const(Val{2}())
│    %844 = Base.literal_pow(%837, %841, %843)::Float64
│    %845 = (%836 + %844)::Float64
│    %846 = (reti@_70 + offsetidx)::Int64
│           Base.setindex!(ret, %845, %846)
│           (@_5 = Base.iterate(%808, %828))
│    %849 = (@_5 === nothing)::Bool
│    %850 = Base.not_int(%849)::Bool
└───        goto #31 if not %850
30 ─        goto #29
31 ┄ %853 = offsetidx::Int64
│    %854 = Core.getfield(#self#, :ref_arcs_to)::Vector{Tuple{Int64, Int64, Int64}}
│    %855 = Main.length(%854)::Int64
│           (offsetidx = %853 + %855)
│    %857 = offsetidx::Int64
│    %858 = Main.length(ret)::Int64
│    %859 = (%857 == %858)::Bool
└───        goto #33 if not %859
32 ─        goto #34
33 ─ %862 = Base.AssertionError("offsetidx == length(ret)")::Any
└───        Base.throw(%862)
34 ┄        return Main.nothing
```

In theory it should be possible to use Enzyme here now.
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

No branches or pull requests

4 participants