Find all 2-transitive groups in SAGE - sage

In GAP I can find all 2-transitive groups from degree 1-31 as follows:
2TransitiveGroups := [];
for n in [1..31] do
Add(2TransitiveGroups, AllTransitiveGroups(DegreeOperation,n,Transitivity,2));
od;
I'm struggling to be able to do this in Sage. I can find all transitive groups (from the GAP database) in Sage as follows:
G = TransitiveGroups(d)
But I don't know how to specify the level of transitivity. I'm sure this must be possible in Sage, as it has GAP's database of transitive groups.
Thanks in advance!

Well. The problem is that probably the easiest way to do this is by just putting your code in GAP via Sage.
st = "2TransitiveGroups := []; for n in [1..3] do Add(2TransitiveGroups, AllTransitiveGroups(DegreeOperation,n,Transitivity,2)); od;"
gap.eval(st)
G = gap("2TransitiveGroups")
G
This yields [ [ ], [ S2 ], [ ] ] but Sage doesn't know what to do with that, as trying G._sage_() will show you.
One could try something more sophisticated using GAP commands like
sage: G = gap.AllTransitiveGroups("DegreeOperation",2,"Transitivity",2)
sage: H = G.Remove(1)
sage: H
S2
But Sage still can't convert this easily to a Sage group for some reason. Ordinarily this answer should tell you how to convert this into a Sage group, but apparently whatever GAP is returning in this list of lists isn't a GAP group somehow. If you can figure out how to get these GAP commands to give you back a group, then H.AsPermGroup() and the question I linked to should work.

Related

Julia: Apply 1 dimensional Julia function to multi-dimensional array

I'm a "write Fortran in all languages" kind of person trying to learn modern programming practices. I have a one dimensional function ft(lx)=HT(x,f(x),lx), where x, and f(x) are one dimensional arrays of size nx, and lx is the size of output array ft. I want to apply HT on a multidimensional array f(x,y,z).
Basically I want to apply HT on all three dimensions to go from f(x,y,z) defined on (nx,ny,nz) dimensional grid, to ft(lx,ly,lz) defined on (lx,ly,lz) dimensional grid:
ft(lx,y,z) = HT(x,f(x,y,z) ,lx)
ft(lx,ly,z) = HT(y,ft(lx,y,z) ,ly)
ft(lx,ly,lz) = HT(z,ft(lx,ly,z),lz)
In f95 style I would tend to write something like:
FTx=zeros((lx,ny,nz))
for k=1:nz
for j=1:ny
FTx[:,j,k]=HT(x,f[:,j,k],lx)
end
end
FTxy=zeros((lx,ly,nz))
for k=1:nz
for i=1:lx
FTxy[i,:,k]=HT(y,FTx[i,:,k],ly)
end
end
FTxyz=zeros((lx,ly,lz))
for j=1:ly
for i=1:lx
FTxyz[i,j,:]=HT(z,FTxy[i,j,:],lz)
end
end
I know idiomatic Julia would require using something like mapslices. I was not able to understand how to go about doing this from the mapslices documentation.
So my question is: what would be the idiomatic Julia code, along with proper type declarations, equivalent to the Fortran style version?
A follow up sub-question would be: Is it possible to write a function
FT = HTnD((Tuple of x,y,z etc.),f(x,y,z), (Tuple of lx,ly,lz etc.))
that works with arbitrary dimensions? I.e. it would automatically adjust computation for 1,2,3 dimensions based on the sizes of input tuples and function?
I have a piece of code here which is fairly close to what you want. The key tool is Base.Cartesian.#nexprs which you can read up on in the linked documentation.
The three essential lines in my code are Lines 30 to 32. Here is a verbal description of what they do.
Line 30: reshape an n1 x n2 x ... nN-sized array C_{k-1} into an n1 x prod(n2,...,nN) matrix tmp_k.
Line 31: Apply the function B[k] to each column of tmp_k. In my code, there are some indirections here since I want to allow for B[k] to be a matrix or a function, but the basic idea is as described above. This is the part where you would want to bring in your HT function.
Line 32: Reshape tmp_k back into an N-dimensional array and circularly permute the dimensions such that the second dimension of tmp_k ends up as the first dimension of C_k. This makes sure that the next iteration of the "loop" implied by #nexprs operates on the second dimension of the original array, and so on.
As you can see, my code avoids forming slices along arbitrary dimensions by permuting such that we only ever need to slice along the first dimension. This makes programming much easier, and it can also have some performance benefits. For example, computing the matrix-vector products B * C[i1,:,i3] for all i1,i3can be done easily and very efficiently by moving the second dimension of C into the first position of tmp and using gemm to compute B * tmp. Doing the same efficiently without the permutation would be much harder.
Following #gTcV's code, your function would look like:
using Base.Cartesian
ht(x,F,d) = mapslices(f -> HT(x, f, d), F, dims = 1)
#generated function HTnD(
xx::NTuple{N,Any},
F::AbstractArray{<:Any,N},
newdims::NTuple{N,Int}
) where {N}
quote
F_0 = F
Base.Cartesian.#nexprs $N k->begin
tmp_k = reshape(F_{k-1},(size(F_{k-1},1),prod(Base.tail(size(F_{k-1})))))
tmp_k = ht(xx[k], tmp_k, newdims[k])
F_k = Array(reshape(permutedims(tmp_k),(Base.tail(size(F_{k-1}))...,size(tmp_k,1))))
# https://github.com/JuliaLang/julia/issues/30988
end
return $(Symbol("F_",N))
end
end
A simpler version, which shows the usage of mapslices would look like this
function simpleHTnD(
xx::NTuple{N,Any},
F::AbstractArray{<:Any,N},
newdims::NTuple{N,Int}
) where {N}
for k = 1:N
F = mapslices(f -> HT(xx[k], f, newdims[k]), F, dims = k)
end
return F
end
you could even use foldl if you are a friend of one-liners ;-)
fold_HTnD(xx, F, newdims) = foldl((F, k) -> mapslices(f -> HT(xx[k], f, newdims[k]), F, dims = k), 1:length(xx), init = F)

Get the mapping from each element of input to the bin of the histogram in Julia

Matlab's [n,mapx] = histc(x, bin_edged) returns the counts of x in each bin as n and returns a map, which is the same length of x which is the bin index that each element of x was placed into.
I can do the same thing in Julia as follows:
Using StatsBase
x = rand(1000)
bin_e = 0:0.1:1
h = fit(Histogram, x, bin_e)
yx = map((z) -> findnext(z.<=h.edges[1],1),x) .- 1
Is this the "right way" to do this? It seem a bit kludgy.
Inspired by this python question you should be able to define a small function that delivers the desired mapping (modulo conventions):
binindices(edges, data) = searchsortedlast.(Ref(edges), data)
Note that the bin edges are sorted and we can use seachsortedlast to get the last bin edge smaller or equal than a datapoint. Broadcasting this over all of the data we obtain the mapping. Note that the Ref(edges) indicates that edges is a scalar under broadcasting (that means that the full array is considered in each call).
Although conceptionally identical to your solution, this approach is about 13x faster on my machine.
I filed an issue over at StatsBase.jl's github page suggesting to add this as a feature.
After looking through the code for Histogram.jl I found that they already included a function binindex. So this solution is probably the best:
x = 0:0.001:10
h1 = fit(Histogram,x,0:10,closed=left)
xmap1 = StatsBase.binindex.(Ref(h1), x)
h2 = fit(Histogram,x,0:10,closed=right)
xmap2 = StatsBase.binindex.(Ref(h2), x)
I stumbled across this question when I was trying to figure out how many occurrences of each value I had in a list of values. If each value is in its own bin (as for categorical data, or integer data with a small number of unique values), this is what one would be plotting in a histogram.
If that is what you want, then countmap() in StatBase package is just what you need.

Julia: How to compute mean by group using aggregate for IndexedTables.jl?

I am trying to use the aggregate function to compute the mean of a variable by group
using Distributions, PooledArrays
N=Int64(2e9/8); K=100;
pool = [#sprintf "id%03d" k for k in 1:K]
pool1 = [#sprintf "id%010d" k for k in 1:(N/K)]
function randstrarray(pool, N)
PooledArray(PooledArrays.RefArray(rand(UInt8(1):UInt8(K), N)), pool)
end
using JuliaDB
DT = IndexedTable(Columns([1:N;]), Columns(
id1 = randstrarray(pool, N),
v3 = rand(round.(rand(Uniform(0,100),100),4), N) # numeric e.g. 23.5749
));
res = IndexedTables.aggregate(mean, DT, by=(:id1,), with=:v3)
How I get the error
MethodError: no method matching mean(::Float64, ::Float64)
Closest candidates are:
mean(!Matched::Union{Function, Type}, ::Any) at statistics.jl:19
mean(!Matched::AbstractArray{T,N} where N, ::Any) where T at statistics.jl:57
mean(::Any) at statistics.jl:34
in at base\<missing>
in #aggregate#144 at IndexedTables\src\query.jl:119
in aggregate_to at IndexedTables\src\query.jl:148
however
IndexedTables.aggregate(+ , DT, by=(:id1,), with=:v3)
works fine
Edit:
res = IndexedTables.aggregate_vec(mean, DT, by=(:id1,), with=:v3)
from help:
help?> IndexedTables.aggregate_vec
aggregate_vec(f::Function, x::IndexedTable)
Combine adjacent rows with equal indices using a function from vector to scalar, e.g. mean.
Old answer:
(I keep it because it was pleasant exercise (for me) how to create helper type and functions if something doesn't work like we want. Maybe it could help someone in future :)
I am not sure how do you like to aggregate mean. My idea is to calculate "center of gravity" for points with equivalent mass.
center of two points: G = (A+B)/2
adding (aggregating) third point C is (2G+C)/3 (2G because G's mass is A's mass +B's mass)
etc.
struct Atractor
center::Float64
mass::Int64
end
" two points create new atractor with double mass "
mediocre(a::Float64, b::Float64) = Atractor((a+b)/2, 2)
# pls forgive me function's name! :)
" aggregate new point to atractor "
function mediocre(a::Atractor, b::Float64)
mass = a.mass + 1
Atractor((a.center*a.mass+b)/mass, mass)
end
Test:
tst_array = rand(Float64, 100);
isapprox(mean(tst_array), reduce(mediocre, tst_array).center)
true # at least in my tests! :)
mean(tst_array) == reduce(mediocre, tst_array).center # sometimes true
For aggregate function we need a little more work:
import Base.convert
" we need method for convert Atractor to Float64 because aggregate
function wants to store result in Float64 "
convert(Float64, x::Atractor) = x.center
And now it (probably :P) works
res = IndexedTables.aggregate(mediocre, DT, by=(:id1,), with=:v3)
id1 │
────────┼────────
"id001" │ 45.9404
"id002" │ 47.0032
"id003" │ 46.0846
"id004" │ 47.2567
...
I hope you see that aggregating mean has impact to precision! (there is more sum and divide operations)
You need to tell it how to reduce two numbers to one. mean is for arrays. So just use an anonymous function:
res = IndexedTables.aggregate((x,y)->(x+y)/2, DT, by=(:id1,), with=:v3)
I'd really like to help you, but it took me 10 minutes to install all the packages and another few minutes to run the code and figuring out what it actually does (or doesn't). It would be great if you'd provide a "minimal working example", which focusses on the problem. In fact, the only requirement to reproduce your problem is seemingly IndexedTables and two random arrays.
(Sorry, this is not a complete answer, but too long to be a comment.)
Anyways, if you read the docstring of IndexedTables.aggregate, you see that it requires a function which takes two arguments and obviously returns a single value::
help?> IndexedTables.aggregate
aggregate(f::Function, arr::IndexedTable)
Combine adjacent rows with equal indices using the given 2-argument
reduction function, returning the result in a new array.
You see in the error message you posted, that there is
no method matching mean(::Float64, ::Float64)
Since I don't know what you expect to be calculated, I now assume that you want to calculate the mean value of the two numbers. In this case you can define another method for mean():
Base.mean(x, y) = (x+y) / 2
This will fulfil the aggregate function signature requirements. But I am not sure if this is what you want.

Choosing an arbitrary dimension to filter over?

In Julia, is there a good way to "choose to loop over an arbitrary dimension" d? For example, I want to apply a diffusion filter to a 2D x I want to do
for j = 1:size(x,2)
for i = 2:size(x,1)-1
x2[i,j] = x[i-1,j] - 2x[i,j] + x[i+1,j]
end
end
But I want to write a function diffFilter(x2,x,d) where x can be an arbitrary dimension array and d is any dimension less than ndims(x), and it applies this x[i-1] + 2x[i] - x[i+1] filter along the dimension d (into x2 without allocating). Any idea how to do the indexing such that I can use that d to have that special part of the loop be the dth index?
You'll want to look at the pair of blog posts that Tim Holy has written on the subject:
http://julialang.org/blog/2016/02/iteration
http://julialang.org/blog/2016/03/arrays-iteration
That should give you a start on the subject.
The standard library function mapslices does this. You can write a function that applies the filter to a vector, and mapslices will take care of applying it to a particular dimension.

maple: plotting result of a numerical dsolve

I have to solve a differential equation numerically; so to say:
diff(y(x), x)+x^2-15*x = 5
with the initial conditions:
inc := y(0) = 0
the solution is of course:
sol := dsolve({f, inc}, numeric);
which results in:
proc(x_rkf45) ... end
Now I want to plot y(x) for x=0..2 for instance.
What shoudl I do?
the code:
plot(sol(x), x = 0 .. 2);
does not work!
Warning, unable to evaluate the function to numeric values in the
region; see the plotting command's help page to ensure the calling
sequence is correct
Here are three different ways to do that.
The first is to use the DEtools[DEplot] command, which both solves and plots. It's input is the differential equation(s) and one or more sets of intitial conditions (as opposed to something that dsolve(...,numeric) returns).
The DEplot command has lots of options. You can turn off inclusion of the field plot, for example.
restart:
deq := diff(y(x), x)+x^2-15*x = 5:
ic := y(0) = 0:
DEtools[DEplot](deq, y(x), x=0..2, [ic]);
The next way is to call dsolve(...,numeric) as you did, and to pass what it returns to the plots:-odeplot command.
sol := dsolve({deq, ic}, numeric):
plots:-odeplot(sol, x=0..2);
Yet another way is to pass dsolve the additional output=listprocedure option so that it returns a list of procedures. Any of those can then be extracted and used to compute at a point or used by passing to the usual plot command.
sollist := dsolve({deq, ic}, numeric, output=listprocedure):
Y := eval(y(x),sollist):
Y(1.0);
12.1666666666667
plot( Y, 0..2 );
See the help pages for DEtools[DEplot], plots:-odeplot or plot,options for more details on customizing the resulting plots.
If you choose to go the odeplot way and also wish to include the field plot then you can augment the plot using plots:-display and plots:-fieldplot.

Resources