Skip to content

Commit 499c2b9

Browse files
authored
Merge pull request #48 from MartaVanin/master
v0.6.0: Polynomials and GMM
2 parents aadc48d + 6986057 commit 499c2b9

File tree

9 files changed

+85
-50
lines changed

9 files changed

+85
-50
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
## Staged
22

3+
## v0.6.0
4+
- drop GaussianMixtures.jl dependency in favour of using MixtureModels from Distributions.jl
5+
- add support for polynomials in mle (adds Polynomials dependency!)
6+
- add `detect_ambiguities` at the end of the tests
7+
- update todo.md
8+
- extends _PMDSE.rand to host AbstractRNG and, e.g., seed! the random sample generation
9+
310
## v0.5.0
411
- add support JuMP 0.22, PMD 0.12,0.13, IM 0.7
512
- nicer ternary operators

Project.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
name = "PowerModelsDistributionStateEstimation"
22
uuid = "d0713e65-ce0c-4a8e-a1da-2ed737d217c5"
33
authors = ["Marta Vanin", "Tom Van Acker"]
4-
version = "0.5.0"
4+
version = "0.6.0"
55

66
[deps]
77
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
88
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
99
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
1010
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
11-
GaussianMixtures = "cc18c42c-b769-54ff-9e2a-b28141a64aae"
1211
InfrastructureModels = "2030c09a-7f63-5d83-885d-db604e0e9cc0"
1312
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
1413
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
1514
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1615
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
1716
LoggingExtras = "e6f89c97-d47a-5376-807f-9c37f3926c36"
1817
Optim = "429524aa-4258-5aef-a3af-852621145aeb"
18+
Polynomials = "f27b6e38-b328-58d1-80ce-0feddd5e7a45"
1919
PowerModelsDistribution = "d7431456-977f-11e9-2de3-97ff7677985e"
2020
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
2121
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
@@ -26,12 +26,12 @@ CSV = "~0.6.1"
2626
DataFrames = "~0.21.0"
2727
Distributions = "0.22.3 - 0.24.14"
2828
ForwardDiff = "~0.10.18"
29-
GaussianMixtures = "0.3.2"
3029
InfrastructureModels = "~0.6, ~0.7"
3130
JSON = "~0.18, ~0.19, ~0.20, ~0.21"
3231
JuMP = "~0.21, ~0.22"
3332
LoggingExtras = "~0.4"
3433
Optim = "~1.2.0"
34+
Polynomials = "~2.0.17"
3535
PowerModelsDistribution = "~0.11, ~0.12, ~0.13"
3636
SpecialFunctions = "~0.10.3"
3737
julia = "^1"

TODO.md

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,43 @@
1-
## A list of things to do for the next release (0.4.1)
1+
## A list of things to do for the next release (0.7.0)
22

3-
- [ ] update basic notebook (add bad data in it), add non-Gaussian notebook and more non-Gaussian docs
3+
- [ ] MAJOR/BREAKING: upgrade CSV
4+
- [ ] MAJOR/BREAKING: after CSV, upgrade PMD, Ipopt, JuMP
5+
- [ ] move LTS to julia 1.6 and drop support for JuMP < 0.22, and consequently PMD/Ipopt versions
6+
7+
- [ ] import/export pdf, etc. from Distributions.jl? simplify residual constraint
8+
9+
- [ ] update basic notebook (add bad data), add non-Gaussian notebook and more non-Gaussian docs
410

511
- [ ] input through array of measurements/dataframe rather than csv
612
- allows to create these directly from powerflow results without creating csv files
713
- replace mktempdir etc. from the tests and use this once it is up and running
14+
- cannot remove CSV dep because of ENWL parsing, but maybe there is a workaround? (like putting ENWL data in another repo except tests, see below)
815

916
- [ ] consider deprecating reduced_ac and reduced_ivr after test against @smart_constraint (especially reduced_ac)
1017

1118
- [ ] increase coverage, in particular:
1219
- test "ls" and "lav" (no weights)
1320
- test for rand(ExtendedBeta)
14-
- test for GMM grad/heslogpdf
21+
- test for mles with various distributions
22+
- test with more measurement conversions
1523
- fix test of line 49-50 in pseudo_measurements.jl: in 50, NUMERICAL_ERROR when sol is correct, in 49 EXCEPTION_ACCESS_VIOLATION at 0x2e2075e6 -- mumps_cst_amf_ in windows CI
16-
- re-introduce start_values in tests and enable ubuntu tests back in CI (and maybe julia 1.0)
24+
- re-introduce start_values in tests and enable ubuntu tests back in CI (julia 1.6 as LTS will move, see below)
1725
- lines 228, 232-234 in bad_data.jl and move atol to 1.1e-3 in with_errors.jl line 186
1826

27+
- [ ] increase dependency bound on Distributions.jl
28+
- [ ] nw_id_default: _IM to _PMD. to rm _IM dep, sol_component_value should should be replaced (ask _PMD people to export it?)
29+
1930
## TODO for future releases/research wishlist
2031

21-
- [ ] try rescale ENWL database per MVA (?)
22-
- [ ] test example notebooks (?)
32+
- [ ] facilitate change ENWL database power_base (?)
33+
- [ ] add tests on example notebooks (?)
2334
- [ ] intuitive/automatic inclusion of load/transfo models (?)
2435
- or MV/LV notebook?
2536
- [ ] additional (convex?) formulations (?)
2637
- [ ] advanced bad data functionalities (?)
2738
- [ ] consider other robust estimators, e.g. schweppe huber(?)
2839
- with MOI/JuMP complementarity constraints ?
2940
- or ConditionalJuMP.jl ? Or other?
30-
- [ ] find/build 4-wire test case and test
41+
- [ ] find/build 4-wire test case and test
42+
- [ ] de-localize ENWL dataset to other repo (except feeders used in tests)
43+
- [ ] impl. own Gauss-Newton solver and matrix functions (?)

src/PowerModelsDistributionStateEstimation.jl

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,17 @@ module PowerModelsDistributionStateEstimation
99

1010
# import pkgs
1111
import Base: maximum, minimum, rand
12-
import CSV
13-
import DataFrames
12+
import CSV, DataFrames
1413
import Distributions
1514
import Distributions: logpdf, gradlogpdf
1615
import ForwardDiff
17-
import GaussianMixtures
1816
import InfrastructureModels
1917
import JuMP
2018
import LinearAlgebra
2119
import LinearAlgebra: diag, diagm, I
2220
import Logging, LoggingExtras
2321
import Optim
22+
import Polynomials as _Poly
2423
import PowerModelsDistribution
2524
import PowerModelsDistribution: _has_nl_expression #need this to use @smart_constraint
2625
import Random
@@ -35,7 +34,6 @@ export optimizer_with_attributes
3534
const _CSV = CSV
3635
const _DFS = DataFrames
3736
const _DST = Distributions
38-
const _GMM = GaussianMixtures
3937
const _IM = InfrastructureModels
4038
const _PMD = PowerModelsDistribution
4139
const _PMDSE = PowerModelsDistributionStateEstimation
@@ -79,6 +77,7 @@ include("form/reduced_ivr.jl")
7977
include("io/distributions.jl")
8078
include("io/measurement_parser.jl")
8179
include("io/network_parser.jl")
80+
include("io/polynomials.jl")
8281
include("io/postprocessing.jl")
8382

8483
include("prob/se.jl")

src/core/constraint.jl

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function constraint_mc_residual(pm::_PMD.AbstractUnbalancedPowerModel, i::Int; n
2323

2424
for (idx, c) in enumerate(conns)
2525
if (occursin("ls", crit) || occursin("lav", crit)) && isa(dst[idx], _DST.Normal)
26-
μ, σ = occursin("w", crit) ? (_DST.mean(dst[idx]), _DST.std(dst[idx])) : (_DST.mean(dst[idx]), 1.0)
26+
μ, σ = occursin("w", crit) ? (_DST.mean(dst[idx]), _DST.std(dst[idx])) : (_DST.mean(dst[idx]), 1.0)
2727
end
2828
if isa(dst[idx], Float64)
2929
JuMP.@constraint(pm.model, var[c] == dst[idx])
@@ -48,11 +48,12 @@ function constraint_mc_residual(pm::_PMD.AbstractUnbalancedPowerModel, i::Int; n
4848
res[idx] * rsc * σ >= - (var[c] - μ)
4949
)
5050
elseif crit == "mle"
51-
pkg_id = ( isa(dst[idx], ExtendedBeta{Float64}) || isa(dst[idx], _GMM.GMM) ) ? _PMDSE : _DST
52-
lb = ( !isa(dst[idx], _GMM.GMM) && !isinf(pkg_id.minimum(dst[idx])) ) ? pkg_id.minimum(dst[idx]) : -10
53-
ub = ( !isa(dst[idx], _GMM.GMM) && !isinf(pkg_id.maximum(dst[idx])) ) ? pkg_id.maximum(dst[idx]) : 10
54-
if isa(dst[idx], _GMM.GMM) lb = _PMD.ref(pm, nw, :meas, i, "min") end
55-
if isa(dst[idx], _GMM.GMM) ub = _PMD.ref(pm, nw, :meas, i, "max") end
51+
#TODO: enforce min and max in the meas dictionary and just with haskey make it optional for extendedbeta
52+
pkg_id = any([ dst[idx] isa d for d in [ExtendedBeta{Float64}, _Poly.Polynomial]]) ? _PMDSE : _DST
53+
lb = ( !isa(dst[idx], _DST.MixtureModel) && !isinf(pkg_id.minimum(dst[idx])) ) ? pkg_id.minimum(dst[idx]) : -10
54+
ub = ( !isa(dst[idx], _DST.MixtureModel) && !isinf(pkg_id.maximum(dst[idx])) ) ? pkg_id.maximum(dst[idx]) : 10
55+
if any([ dst[idx] isa d for d in [_DST.MixtureModel, _Poly.Polynomial]]) lb = _PMD.ref(pm, nw, :meas, i, "min") end
56+
if any([ dst[idx] isa d for d in [_DST.MixtureModel, _Poly.Polynomial]]) ub = _PMD.ref(pm, nw, :meas, i, "max") end
5657

5758
shf = abs(Optim.optimize(x -> -pkg_id.logpdf(dst[idx],x),lb,ub).minimum)
5859
f = Symbol("df_",i,"_",c)

src/io/distributions.jl

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,10 @@ end
136136
rand(dst::ExtendedBeta, N::Int) =
137137
(dst.max - dst.min) .* rand(_DST.Beta(dst.α, dst.β), N) .+ dst.min
138138

139-
## Gamma
140-
# functions
139+
rand(r::_RAN.AbstractRNG, dst::ExtendedBeta, N::Int) =
140+
(dst.max - dst.min) .* rand(r, _DST.Beta(dst.α, dst.β), N) .+ dst.min
141+
142+
## Gamma heslogpdf
141143
function heslogpdf(d::_DST.Gamma{T}, x::Real) where T<:Real
142144
if _DST.insupport(_DST.Gamma, x)
143145
α, θ = _DST.params(d)
@@ -146,30 +148,24 @@ function heslogpdf(d::_DST.Gamma{T}, x::Real) where T<:Real
146148
zero(T)
147149
end
148150
end
149-
150-
## logpdf, gradlogpdf and heslogpdf for Gaussian mixture
151-
# The GMM type is from package GaussianMixtures
152-
function logpdf(d::_GMM.GMM{Float64,Array{Float64,2}}, x::Real)
153-
σ = sqrt.(d.Σ)
154-
μ = d.μ
155-
g = _DST.Normal.(μ, σ)
156-
log(sum([d.w[i]*_DST.pdf(g[i],x) for i in 1:d.n]))
157-
end
158-
159-
function gradlogpdf(d::_GMM.GMM{Float64,Array{Float64,2}}, x::Real)
160-
σ = sqrt.(d.Σ)
161-
μ = d.μ
162-
γ = d.w./(sqrt(2*π)*σ)
163-
sum([-γ[i]*(x-μ[i])*exp(-0.5*(x-μ[i])^2/σ[i]^2)*σ[i]^(-2) for i in 1:d.n])/sum([γ[i]*exp(-0.5*(x-μ[i])^2/σ[i]^2) for i in 1:d.n])
151+
##
152+
# gradlogpdf and heslogpdf for Gaussian mixtures done directly
153+
# with Distributions.jl
154+
function gradlogpdf(d::_DST.MixtureModel, x::Real)
155+
@assert all([c isa _DST.Normal for c in d.components]) "Only Gaussian mixture models are supported!"
156+
σ = [c.σ for c in d.components]
157+
μ = [c.μ for c in d.components]
158+
γ = d.prior.p./(sqrt(2*π)*σ)
159+
sum([-γ[i]*(x-μ[i])*exp(-0.5*(x-μ[i])^2/σ[i]^2)*σ[i]^(-2) for i in 1:length(d.components)])/sum([γ[i]*exp(-0.5*(x-μ[i])^2/σ[i]^2) for i in 1:length(d.components)])
164160
end
165161

166-
function heslogpdf(d::_GMM.GMM{Float64,Array{Float64,2}}, x::Real)
167-
σ = sqrt.(d.Σ)
168-
μ = d.μ
169-
γ = d.w./(sqrt(2*π)*σ)
170-
p1 = sum([γ[i]*(x-μ[i])^2*exp(-0.5*(x-μ[i])^2/σ[i]^2)*σ[i]^(-4) - γ[i]*exp(-0.5*(x-μ[i])^2/σ[i]^2)*σ[i]^(-2) for i in 1:d.n])
171-
p2 = sum([γ[i]*exp(-0.5*(x-μ[i])^2/σ[i]^2) for i in 1:d.n])
172-
p3 = sum([-γ[i]*(x-μ[i])*exp(-0.5*(x-μ[i])^2/σ[i]^2)*σ[i]^(-2) for i in 1:d.n])^2
173-
p4 = sum([γ[i]*exp(-0.5*(x-μ[i])^2/σ[i]^2) for i in 1:d.n])^2
162+
function heslogpdf(d::_DST.MixtureModel, x::Real)
163+
σ = [c.σ for c in d.components]
164+
μ = [c.μ for c in d.components]
165+
γ = d.prior.p./(sqrt(2*π)*σ)
166+
p1 = sum([γ[i]*(x-μ[i])^2*exp(-0.5*(x-μ[i])^2/σ[i]^2)*σ[i]^(-4) - γ[i]*exp(-0.5*(x-μ[i])^2/σ[i]^2)*σ[i]^(-2) for i in 1:length(d.components)])
167+
p2 = sum([γ[i]*exp(-0.5*(x-μ[i])^2/σ[i]^2) for i in 1:length(d.components)])
168+
p3 = sum([-γ[i]*(x-μ[i])*exp(-0.5*(x-μ[i])^2/σ[i]^2)*σ[i]^(-2) for i in 1:length(d.components)])^2
169+
p4 = sum([γ[i]*exp(-0.5*(x-μ[i])^2/σ[i]^2) for i in 1:length(d.components)])^2
174170
return p1/p2-p3/p4
175171
end

src/io/polynomials.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# maximum(p::_Poly.Polynomial) = Base.maximum(p)
2+
# minimum(p::_Poly.Polynomial) = Base.minimum(p)
3+
pdf(p::_Poly.Polynomial) = error("`pdf` of a polynomial is not definied because it is expected that the polynomial used is an approximation of the `logpdf` instead!")
4+
logpdf(p::_Poly.Polynomial, x::Real) = p(x)
5+
gradlogpdf(p::_Poly.Polynomial, x::Real) = _Poly.derivative(p, 1)(x)
6+
heslogpdf(p::_Poly.Polynomial, x::Real) = _Poly.derivative(p, 2)(x)

test/distributions.jl

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,22 @@ dst = ExtendedBeta(2.0,4.0,10.0,100.0)
2626
@test isapprox(_PMDSE.heslogpdf(dst,50.0), -0.001825, atol=1e-8)
2727

2828
# Testing logpdf/gradlogpdf/heslogpdf of gaussian mixture models
29-
beta = _PMDSE.ExtendedBeta(1.6339, 20.9022, -1.0e-6, 8.268e-5)
30-
gmm = _GMM.GMM(1, rand(beta, 100000))
31-
g1 = _DST.Normal(gmm.μ[1], sqrt(gmm.Σ[1]))
29+
beta = _PMDSE.ExtendedBeta(1.6339, 20.9022, 0.001, 2.0)
30+
gmm = _DST.MixtureModel([_DST.Normal(0.14585265, sqrt(0.01144241))], [1.0])
31+
g1 = _DST.Normal(gmm.components[1].μ, gmm.components[1].σ)
3232
@test isapprox(_DST.logpdf(g1, beta.min), _PMDSE.logpdf(gmm, beta.min), atol = 1e-8)
3333
@test isapprox(_DST.logpdf(g1, beta.max), _PMDSE.logpdf(gmm, beta.max), atol = 1e-8)
3434
@test isapprox(_DST.gradlogpdf(g1, beta.min), _PMDSE.gradlogpdf(gmm, beta.min), atol = 1e-8)
3535
@test isapprox(_DST.gradlogpdf(g1, beta.max), _PMDSE.gradlogpdf(gmm, beta.max), atol = 1e-8)
3636
@test isapprox(_PMDSE.heslogpdf(g1, beta.min), _PMDSE.heslogpdf(gmm, beta.min), atol = 1e-2)
3737
@test isapprox(_PMDSE.heslogpdf(g1, beta.max), _PMDSE.heslogpdf(gmm, beta.max), atol = 1e-2)
3838

39+
p = _Poly.Polynomial([0,1,1])
40+
@test isapprox(_PMDSE.logpdf(p, 0.0), 0.0, atol=1e-8)
41+
@test isapprox(_PMDSE.logpdf(p, 1.0), 2.0, atol=1e-8)
42+
@test isapprox(_PMDSE.gradlogpdf(p, 1.0), 3.0, atol=1e-8)
43+
@test isapprox(_PMDSE.gradlogpdf(p, 0.0), 1.0, atol=1e-8)
44+
@test isapprox(_PMDSE.heslogpdf(p, 0.0), 2.0, atol=1e-8)
45+
@test isapprox(_PMDSE.heslogpdf(p, 1.0), 2.0, atol=1e-8)
46+
3947
end

test/runtests.jl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,20 @@
77
################################################################################
88

99
# using pkgs
10-
using Distributions, GaussianMixtures
10+
using Distributions
1111
using HDF5
1212
using Ipopt
13+
using Polynomials
1314
using PowerModels, PowerModelsDistribution
1415
using PowerModelsDistributionStateEstimation
1516
#using SCS #removed while SDP tests are not active
1617
using Test
1718

1819
# pkg const
1920
const _DST = Distributions
20-
const _GMM = GaussianMixtures
2121
const _PMD = PowerModelsDistribution
2222
const _PMDSE = PowerModelsDistributionStateEstimation
23+
const _Poly = Polynomials
2324

2425
#network and feeder from ENWL for tests
2526
ntw, fdr = 4, 2
@@ -54,3 +55,7 @@ ipopt_solver = optimizer_with_attributes(Ipopt.Optimizer,"max_cpu_time"=>300.0,
5455
include("with_errors.jl")
5556

5657
end
58+
ambiguities = Test.detect_ambiguities(PowerModelsDistributionStateEstimation);
59+
if !isempty(ambiguities)
60+
println("ambiguities detected: $ambiguities")
61+
end

0 commit comments

Comments
 (0)