From 94c2d959ddeff8d1ffb618cc8d620f15bd7d43cc Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Wed, 23 May 2018 14:16:34 +0200 Subject: [PATCH 01/10] Bump Documenter version to 0.18.0 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f2600f22..ce7e96d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,9 +27,9 @@ after_success: using Pkg # `using Pkg` and `pkg"..."` must be in separate if blocks end @static if VERSION >= v"0.7.0-DEV.3656" - pkg"add Documenter@0.13.2" + pkg"add Documenter@0.18.0" else - Pkg.add("Documenter", v"0.13.2", v"0.13.2+") + Pkg.add("Documenter", v"0.18.0", v"0.18.0+") cd(Pkg.dir("ACME")) end include(joinpath("docs", "make.jl"))' From d264ba39f6af314a27e9b122f8be0e1b9425c977 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Wed, 23 May 2018 14:17:26 +0200 Subject: [PATCH 02/10] Use doctests in gettingstarted Fix a few deprecations while at it. --- docs/src/gettingstarted.md | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/docs/src/gettingstarted.md b/docs/src/gettingstarted.md index 4d9dcae1..8fc198ef 100644 --- a/docs/src/gettingstarted.md +++ b/docs/src/gettingstarted.md @@ -19,13 +19,16 @@ This will download ACME and all of its dependencies. We will demonstrate ACME by modeling a simple diode clipper. The first step is to load ACME: -```Julia +```jldoctest firststeps; output = false using ACME + +# output + ``` Now we create the circuit description: -```Julia +```jldoctest firststeps; output = false, filter = r"(ACME\.)?Circuit\(.*"s circ = @circuit begin j_in = voltagesource() r1 = resistor(1e3) @@ -38,6 +41,10 @@ circ = @circuit begin r1[2] ⟷ c1[1] ⟷ d1[+] ⟷ d2[-] ⟷ j_out[+] gnd ⟷ c1[2] ⟷ d1[-] ⟷ d2[+] ⟷ j_out[-] end + +# output + +Circuit(...) ``` The first six lines inside the `begin`/`end` block instantiate circuit elements. @@ -63,7 +70,7 @@ It is also possible to specify connections following the element definition one can only connect to elements defined before. Thus, above circuit could also be entered as: -```Julia +```jldoctest firststeps; output = false, filter = r"(ACME\.)?Circuit\(.*"s circ = @circuit begin j_in = voltagesource(), [-] ⟷ gnd r1 = resistor(1e3), [1] ⟷ j_in[+] @@ -72,13 +79,21 @@ circ = @circuit begin d2 = diode(is=1.8e-15), [+] ⟷ gnd, [-] ⟷ r1[2] j_out = voltageprobe(), [+] ⟷ r1[2], [-] ⟷ gnd end + +# output + +Circuit(...) ``` Now that the circuit has been set up, we need to turn it into a model. This could hardly be any easier: -```Julia -model = DiscreteModel(circ, 1./44100) +```jldoctest firststeps; output = false, filter = r"(ACME\.)?DiscreteModel{.*"s +model = DiscreteModel(circ, 1/44100) + +# output + +DiscreteModel{...}(...) ``` The second argument specifies the sampling interval, the reciprocal of the @@ -86,10 +101,16 @@ sampling rate, here assumed to be the typical 44100 Hz. Now we can process some input data. It has to be provided as a matrix with one row per input (just one in the example) and one column per sample. So for a -sinusoid at 1 kHz lasting one second, we do:: +sinusoid at 1 kHz lasting one second, we do: -```Julia -y = run!(model, sin(2π*1000/44100*(0:44099)')) +```jldoctest firststeps; filter = r"\r?Running model:.*" +y = run!(model, sin.(2π*1000/44100*(0:44099)')) + +# output + +Running model: 100%|████████████████████████████████████| Time: 0:00:01 +1×44100 Array{Float64,2}: + 0.0 0.0275964 0.0990996 0.195777 … -0.537508 -0.462978 -0.36521 ``` The output `y` now likewise is a matrix with one row for the one probe we have From 36471637f866fa3217aa96523a0c1555c5688bc8 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Thu, 24 May 2018 09:39:10 +0200 Subject: [PATCH 03/10] Use doctests in user guide Some minor fixes and additions along the way. --- docs/src/ug.md | 76 ++++++++++++++++++++++++++++++++++++++++++++------ src/circuit.jl | 30 ++++++++++++++------ 2 files changed, 89 insertions(+), 17 deletions(-) diff --git a/docs/src/ug.md b/docs/src/ug.md index 2e924225..0c7a023d 100644 --- a/docs/src/ug.md +++ b/docs/src/ug.md @@ -24,7 +24,7 @@ disconnect! ``` For example, a cascade of 20 RC-lowpasses could be generated by: -```julia +```jldoctest ug; output = false, setup = :(using ACME) circ = @circuit begin src = voltagesource(), [-] ⟷ gnd output = voltageprobe(), [-] ⟷ gnd @@ -36,9 +36,12 @@ for i in 1:20 connect!(circ, (resrefdes, 1), pin) connect!(circ, (resrefdes, 2), (caprefdes, 1)) connect!(circ, (caprefdes, 2), :gnd) - pin = (resrefdes, 2) + global pin = (resrefdes, 2) end connect!(circ, pin, (:output, +)) + +# output + ``` ## Model Creation and Use @@ -47,35 +50,63 @@ A `Circuit` only stores elements and information about their connections. To simulate a circuit, a model has to be derived from it. This can be as simple as: -```Julia +```jldoctest ug; output = false, filter = r"(ACME\.)?DiscreteModel{.*"s model = DiscreteModel(circ, 1/44100) + +# output + +DiscreteModel{...}(...) ``` Here, `1/44100` denotes the sampling interval, i.e. the reciprocal of the sampling rate at which the model should run. Optionally, one can specify the solver to use for solving the model's non-linear equation: -```Julia +```jldoctest ug; output = false, filter = r"(ACME\.)?DiscreteModel{.*"s model = DiscreteModel(circ, 1/44100, HomotopySolver{SimpleSolver}) + +# output + +DiscreteModel{...}(...) ``` See [Solvers](@ref) for more information about the available solvers. Once a model is created, it can be run: -```Julia +```jldoctest; output = false, setup = :(using ACME; model=DiscreteModel(@circuit(begin end), 1); u=zeros(0,10)) y = run!(model, u) + +# output + +0×10 Array{Float64,2} ``` The input `u` is matrix with one row for each of the circuit's inputs and one column for each time step to simulate. Likewise, the output `y` will be a matrix with one row for each of the circuit's outputs and one column for each simulated time step. The order of the rows will correspond to the order in which -the respective input and output elements were added to the `Circuit`. To +the respective input and output elements were added to the `Circuit`. +So for above circuit, we may obtain the first 100 samples of the impulse +response with + +```jldoctest ug +run!(model, [1 zeros(1,99)]) + +# output + +1×100 Array{Float64,2}: + 1.83357e-8 3.1622e-7 2.59861e-6 … 0.00465423 0.00459275 0.00453208 +``` +To simulate a circuit without inputs, a matrix with zero rows may be passed: -```Julia +```jldoctest; output = false, setup = :(using ACME; model=DiscreteModel(@circuit(begin end), 1)) y = run!(model, zeros(0, 100)) + +# output + +0×100 Array{Float64,2} ``` The internal state of the model (e.g. capacitor charges) is preserved accross @@ -85,9 +116,12 @@ Each invocation of `run!` in this way has to allocate some memory as temporary storage. To avoid this overhead when running the same model for many small input blocks, a `ModelRunner` instance can be created explicitly: -```Julia +```jldoctest ug; output = false, setup = :(u=zeros(1,10); y=zeros(1,10)) runner = ModelRunner(model, false) run!(runner, y, u) + +# output + ``` By using a pre-allocated output `y` as in the example, allocations in `run!` are @@ -97,8 +131,32 @@ Upon creation of a `DiscreteModel`, its internal states (e.g. capacitor charges) are set to zero. It is also possible to set the states to a steady state (if one can be found) with: -```Julia +```jldoctest ug; output = false steadystate!(model) + +# output + +20-element Array{Float64,1}: + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 ``` This is often desirable for circuits where bias voltages are only slowly diff --git a/src/circuit.jl b/src/circuit.jl index 5aeaa1c5..21ac2bcf 100644 --- a/src/circuit.jl +++ b/src/circuit.jl @@ -193,11 +193,14 @@ a `Symbol` as needed. # Example -```julia +```jldoctest; output = false, setup = :(using ACME) circ = Circuit() add!(circ, :r, resistor(1e3)) -add!(circ, :src, voltages(5)) +add!(circ, :src, voltagesource(5)) connect!(circ, (:src, -), (:r, 2), :gnd) # connect to gnd net + +# output + ``` """ function connect!(c::Circuit, pins::Union{Pin,Symbol,Tuple{Symbol,Any}}...) @@ -295,7 +298,7 @@ end topomat(incidence::SparseMatrixCSC{<:Integer}) = topomat!(copy(incidence)) topomat(c::Circuit) = topomat!(incidence(c)) -""" +@doc doc""" @circuit begin #= ... =# end Provides a simple domain-specific language to decribe circuits. The @@ -306,13 +309,17 @@ Provides a simple domain-specific language to decribe circuits. The # Example To create a circuit with a voltage source connected to a resistor: -```julia +```jldoctest; output = false, setup = :(using ACME), filter = r"(ACME\.)?Circuit\(.*"s @circuit begin src = voltagesource(5) r = resistor(1000) src[+] ⟷ r[1] src[-] ⟷ r[2] end + +# output + +Circuit(...) ``` Alternatively, connection specifications can be given after an element @@ -321,11 +328,15 @@ defaulting to the current element. # Example -```julia +```jldoctest; output = false, setup = :(using ACME), filter = r"(ACME\.)?Circuit\(.*"s @circuit begin src = voltagesource(5) r = resistor(1000), src[+] ⟷ [1], src[-] ⟷ [2] end + +# output + +Circuit(...) ``` Finally, a connection endpoint may simply be of the form `netname`, to connect @@ -333,11 +344,15 @@ to a named net. (Such named nets are created as needed.) # Example -```julia +```jldoctest; output = false, setup = :(using ACME), filter = r"(ACME\.)?Circuit\(.*"s @circuit begin src = voltagesource(5), [-] ⟷ gnd r = resistor(1000), [1] ⟷ src[+], [2] ⟷ gnd end + +# output + +Circuit(...) ``` If a net or pin specification is not just a single symbol or number, and has to @@ -345,8 +360,7 @@ be put in quotes (e.g. `"in+"`, `"9V"`) !!! note Instead of `⟷` (`\\longleftrightarrow`), one can also use `==`. -""" -macro circuit(cdef) +""" macro circuit(cdef) is_conn_spec(expr::Expr) = (expr.head === :call && (expr.args[1] === :(⟷) || expr.args[1] === :(↔) || expr.args[1] === :(==))) || (expr.head === :comparison && all(c -> c === :(==), expr.args[2:2:end])) From 431e1da459801528955d760ecdbe9a1b30a508e0 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Tue, 10 Jul 2018 13:55:51 +0200 Subject: [PATCH 04/10] Test on both 0.7 and nightly --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index ce7e96d8..717ba123 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: julia julia: - 0.6 - 0.7 + - nightly notifications: email: false webhooks: From 92308931d913dc3aefbb0bdbecaec501fc8d44a1 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Tue, 10 Jul 2018 13:55:22 +0200 Subject: [PATCH 05/10] Avoid applying `findnz` on anything not a `SparseVector` In particular, don't apply `findnz` on an `Adjoint{_,SparseVector}` as it is deprecated on Julia 0.7. --- src/ACME.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ACME.jl b/src/ACME.jl index c1f410a4..13642022 100644 --- a/src/ACME.jl +++ b/src/ACME.jl @@ -721,7 +721,7 @@ function gensolve(a, b, x, h, thresh=0.1) for i in 1:m ait = a[t[i],:]' # ait is a row of the a matrix s = ait * h; - inz, jnz, nz_vals = findnz(s) + jnz, nz_vals = findnz(s') nz_abs_vals = abs.(nz_vals) max_abs_val = reduce(max, zero(eltype(s)), nz_abs_vals) if max_abs_val ≤ tol # cosidered numerical zero From bb8425cae73fd7669a1670102ec040d34f58470b Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Tue, 10 Jul 2018 14:35:10 +0200 Subject: [PATCH 06/10] Use `reduce` with `init` keyword arg --- REQUIRE | 2 +- src/ACME.jl | 8 ++++---- src/circuit.jl | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/REQUIRE b/REQUIRE index 9c40a6eb..a7391c91 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,5 +1,5 @@ julia 0.6 -Compat 0.65.0 +Compat 1.0.0 DataStructures 0.2.9 IterTools 0.1.0 ProgressMeter 0.2.1 diff --git a/src/ACME.jl b/src/ACME.jl index 13642022..83e552d0 100644 --- a/src/ACME.jl +++ b/src/ACME.jl @@ -460,7 +460,7 @@ np(model::DiscreteModel, subidx) = size(model.dqs[subidx], 1) nu(model::DiscreteModel) = size(model.b, 2) ny(model::DiscreteModel) = length(model.y0) nn(model::DiscreteModel, subidx) = size(model.fqs[subidx], 2) -nn(model::DiscreteModel) = reduce(+, 0, size(fq, 2) for fq in model.fqs) +nn(model::DiscreteModel) = Compat.reduce(+, init=0, size(fq, 2) for fq in model.fqs) function steadystate(model::DiscreteModel, u=zeros(nu(model))) @static if VERSION < v"0.7.0-DEV.5211" @@ -528,8 +528,8 @@ function linearize(model::DiscreteModel, usteady::AbstractVector{Float64}=zeros( zranges[idx] = zoff:zoff+length(zsub)-1 fqdzdps = [model.fqprevs[idx][:,zranges[n]] * dzdps[n] for n in 1:idx-1] - dqlins[idx] = reduce(+, model.dqs[idx], fqdzdps .* dqlins[1:idx-1]) - eqlins[idx] = reduce(+, model.eqs[idx], fqdzdps .* eqlins[1:idx-1]) + dqlins[idx] = Compat.reduce(+, init=model.dqs[idx], fqdzdps .* dqlins[1:idx-1]) + eqlins[idx] = Compat.reduce(+, init=model.eqs[idx], fqdzdps .* eqlins[1:idx-1]) x0 += model.c[:,zranges[idx]] * (zsub - dzdps[idx]*psteady) a += model.c[:,zranges[idx]] * dzdps[idx] * dqlins[idx] @@ -723,7 +723,7 @@ function gensolve(a, b, x, h, thresh=0.1) s = ait * h; jnz, nz_vals = findnz(s') nz_abs_vals = abs.(nz_vals) - max_abs_val = reduce(max, zero(eltype(s)), nz_abs_vals) + max_abs_val = Compat.reduce(max, init=zero(eltype(s)), nz_abs_vals) if max_abs_val ≤ tol # cosidered numerical zero continue end diff --git a/src/circuit.jl b/src/circuit.jl index 21ac2bcf..88b5fdc3 100644 --- a/src/circuit.jl +++ b/src/circuit.jl @@ -17,7 +17,7 @@ end elements(c::Circuit) = values(c.elements) for n in [:nb; :nx; :nq; :nu; :nl; :ny; :nn] - @eval ($n)(c::Circuit) = reduce(+, 0, $n(elem) for elem in elements(c)) + @eval ($n)(c::Circuit) = Compat.reduce(+, init=0, $n(elem) for elem in elements(c)) end for mat in [:mv; :mi; :mx; :mxd; :mq; :mu; :pv; :pi; :px; :pxd; :pq] From f26143245bafdc413dea075990c3c0ad3988e1ea Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Tue, 10 Jul 2018 14:58:48 +0200 Subject: [PATCH 07/10] Use `mapslices` with `dims` keyword --- src/ACME.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ACME.jl b/src/ACME.jl index 83e552d0..caaa7fd0 100644 --- a/src/ACME.jl +++ b/src/ACME.jl @@ -716,7 +716,7 @@ function gensolve(a, b, x, h, thresh=0.1) if m == 0 return x, h end - t = sortperm(vec(mapslices(ait -> count(!iszero, ait), a, 2))) # row indexes in ascending order of nnz + t = sortperm(vec(mapslices(ait -> count(!iszero, ait), a, dims=2))) # row indexes in ascending order of nnz tol = 3 * max(eps(float(eltype(a))), eps(float(eltype(h)))) * size(a, 2) for i in 1:m ait = a[t[i],:]' # ait is a row of the a matrix @@ -728,7 +728,7 @@ function gensolve(a, b, x, h, thresh=0.1) continue end jat = jnz[nz_abs_vals .≥ thresh*max_abs_val] # cols above threshold - j = jat[argmin(vec(mapslices(hj -> count(!iszero, hj), h[:,jat], 1)))] + j = jat[argmin(vec(mapslices(hj -> count(!iszero, hj), h[:,jat], dims=1)))] q = h[:,j] x = x + convert(typeof(x), q * ((b[t[i],:]' - ait*x) * (1 / (ait*q)))) if size(h)[2] > 1 From afea173eb45da6a7383532fbc283f64c1fdb71f3 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Wed, 11 Jul 2018 08:44:25 +0200 Subject: [PATCH 08/10] Use `mean` (and `var`) from `Compat.Statistics` (instead of `StatsBase`) --- REQUIRE | 1 - src/kdtree.jl | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/REQUIRE b/REQUIRE index a7391c91..a6ac5502 100644 --- a/REQUIRE +++ b/REQUIRE @@ -3,4 +3,3 @@ Compat 1.0.0 DataStructures 0.2.9 IterTools 0.1.0 ProgressMeter 0.2.1 -StatsBase 0.23.0 diff --git a/src/kdtree.jl b/src/kdtree.jl index 37e5c709..8cbf6cb8 100644 --- a/src/kdtree.jl +++ b/src/kdtree.jl @@ -4,7 +4,7 @@ import Base.deleteat! import Base.isless import Base.isempty -using StatsBase: var +using Compat.Statistics: mean, var mutable struct KDTree{Tcv<:AbstractVector,Tp<:AbstractMatrix} cut_dim::Vector{Int} @@ -41,7 +41,7 @@ function KDTree(p::AbstractMatrix, Np=size(p,2)) @static if VERSION ≥ v"0.7.0-DEV.4064" dim = argmax(vec(var(p[:,1:Np], dims=2))) else - dim = argmax(vec(var(p[:,1:Np], 2))) + dim = argmax(vec(Base.var(p[:,1:Np], 2))) end p_idx = sortperm(vec(p[dim,:])) @@ -63,7 +63,7 @@ function KDTree(p::AbstractMatrix, Np=size(p,2)) @static if VERSION ≥ v"0.7.0-DEV.4064" dim = argmax(vec(var(p[:,p_idx[min_idx[n]:max_idx[n]]], dims=2))) else - dim = argmax(vec(var(p[:,p_idx[min_idx[n]:max_idx[n]]], 2))) + dim = argmax(vec(Base.var(p[:,p_idx[min_idx[n]:max_idx[n]]], 2))) end idx = sortperm(vec(p[dim,p_idx[min_idx[n]:max_idx[n]]])) p_idx[min_idx[n]:max_idx[n]] = p_idx[idx .+ min_idx[n] .- 1] From db2583e6acf43efe99c5d051162494813cd75ea0 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Fri, 20 Jul 2018 09:50:07 +0200 Subject: [PATCH 09/10] Bump used Documneter version to 0.19.0 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 717ba123..efe372b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,9 +28,9 @@ after_success: using Pkg # `using Pkg` and `pkg"..."` must be in separate if blocks end @static if VERSION >= v"0.7.0-DEV.3656" - pkg"add Documenter@0.18.0" + pkg"add Documenter@0.19.0" else - Pkg.add("Documenter", v"0.18.0", v"0.18.0+") + Pkg.add("Documenter", v"0.19.0", v"0.19.0+") cd(Pkg.dir("ACME")) end include(joinpath("docs", "make.jl"))' From 2f9f9936653678295cfd8850675563545e00c9a4 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Tue, 24 Jul 2018 09:47:33 +0200 Subject: [PATCH 10/10] Bump version to 0.7.4 --- README.md | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1fd438bf..e3a5148d 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,7 @@ # ACME.jl - Analog Circuit Modeling and Emulation for Julia [![Join the chat at https://gitter.im/HSU-ANT/ACME.jl](https://badges.gitter.im/HSU-ANT/ACME.jl.svg)](https://gitter.im/HSU-ANT/ACME.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Documentation](https://img.shields.io/badge/docs-stable-blue.svg)](https://hsu-ant.github.io/ACME.jl/stable) -[![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://hsu-ant.github.io/ACME.jl/latest) - -[![ACME](http://pkg.julialang.org/badges/ACME_0.5.svg)](http://pkg.julialang.org/?pkg=ACME) -[![ACME](http://pkg.julialang.org/badges/ACME_0.6.svg)](http://pkg.julialang.org/?pkg=ACME) -[![ACME](http://pkg.julialang.org/badges/ACME_0.7.svg)](http://pkg.julialang.org/?pkg=ACME) - -[![Build Status](https://travis-ci.org/HSU-ANT/ACME.jl.svg?branch=develop)](https://travis-ci.org/HSU-ANT/ACME.jl) -[![codecov](https://codecov.io/gh/HSU-ANT/ACME.jl/branch/develop/graph/badge.svg)](https://codecov.io/gh/HSU-ANT/ACME.jl) -[![Coverage Status](https://coveralls.io/repos/github/HSU-ANT/ACME.jl/badge.svg?branch=develop)](https://coveralls.io/github/HSU-ANT/ACME.jl) +[![Documentation](https://img.shields.io/badge/docs-v0.7.4-blue.svg)](https://hsu-ant.github.io/ACME.jl/v0.7.4) ACME is a [Julia](http://julialang.org/) package for the simulation of electrical circuits, focusing on audio effect circuits. It allows to @@ -131,7 +122,7 @@ fail to run altogether. ## Moving on -There is some [documentation](https://hsu-ant.github.io/ACME.jl/latest) +There is some [documentation](https://hsu-ant.github.io/ACME.jl/v0.7.4) available for how to use ACME. Additionally, you can take a look at the examples that can be found in the `examples` directory below `Pkg.dir("ACME")`.