Julia struct error "no method matching iterate" - struct

While trying to understand parametric structs in Julia, I defined following struct as a subtype of AbstractSet
struct MySet{T} <: AbstractSet{T}
st::Vector{T}
MySet(x::Vector{T}) where {T} = new{T}(x)
end
However, I get following error when I try to create a new object of type MySet
julia> MySet([1,2])
MethodError: no method matching iterate(::MySet{Int64})
Closest candidates are:
iterate(::Union{LinRange, StepRangeLen}) at ~/julia-1.7.1-linux-x86_64/julia-
1.7.1/share/julia/base/range.jl:826
iterate(::Union{LinRange, StepRangeLen}, ::Integer) at ~/julia-1.7.1-linux-
x86_64/julia-1.7.1/share/julia/base/range.jl:826
iterate(::T) where T<:Union{Base.KeySet{<:Any, <:Dict},
Base.ValueIterator{<:Dict}} at ~/julia-1.7.1-linux-x86_64/julia-
1.7.1/share/julia/base/dict.jl:695
Can someone help me understand why I am getting this error.

The MySet object gets created as you can see here:
julia> x = MySet([1,2]);
julia> x.st
2-element Vector{Int64}:
1
2
The problem is that it is not displayed correctly. The reason is that AbstractSet have a custom show method that gets called for them which assumes that your AbstractSet object is iterable.
What you can do to learn what minimal set of methods you should implement for AbstractSet until it gets documented is by writing:
julia> subtypes(AbstractSet)
5-element Vector{Any}:
Base.IdSet
Base.KeySet
BitSet
Set
Test.GenericSet
julia> using Test
julia> methodswith(GenericSet)
[1] isempty(s::GenericSet) in Test
[2] iterate(s::GenericSet, state...) in Test
[3] length(s::GenericSet) in Test
The reason I pick GenericSet is that it is documented as:
help?> GenericSet
search: GenericSet GenericString GenericDict GenericOrder GenericArray
The GenericSet can be used to test generic set APIs that program to
the AbstractSet interface, in order to ensure that functions can work
with set types besides the standard Set and BitSet types.
so what GenericSet defines should be the minimum that AbstractSet interface requires.
(above I show you the full process you could use to discover what is needed, as guessing that GenericSet should be checked is non-obvious without the previous steps)

Related

Python typing: alias vs NewType best practices?

I want to define my own type T for a MutableMapping[str, str]. AFAIK it can be done y type aliasing, or via NewType
from typing import MutableMapping, NewType
T_alias = MutableMapping[str, str]
T_new = NewType("T_new", MutableMapping[str, str])
The problem is the required syntax to initialize one variable of each kind. The correct initialization (which does not trigger any error in Pylance/Pyright) is:
vn: T_new
va: T_alias
vn = T_new({})
va = {}
The assignment of vn looks ok to me (although I find a bit awkward to have to pass the empty dict {}). However I don't like the assignment of va because it does not make clear that va is of type T_alias.
I tried:
va = T_alias({})
but it does not work (Expected no arguments to "MutableMapping" constructor). So I tried also:
va = T_alias()
but it did not work either. Although Pylance does not trigger any error (which may be considered a Pylance bug?), an exception happens at runtime: TypeError: Can't instantiate abstract class MutableMapping with abstract methods __delitem__, __getitem__, __iter__, __len__, __setitem__
So the only way to intialize va making explicit that it is of type T_alias is using typing.cast:
va = cast(T_alias, {})
which I find awkward too.
So the question is: am I undestanding correctly the use of type aliases and NewType? And also, which one would be advised for this particular case? I find the initialization of T_new objects cleaner (I don't like the cast()) but, I'm worried that the NewType() would introduce some overhead when dealing with these objects, and I don't really need to sublcass the dict, so I would prefer to use the alias.

Reading class method definition - what is Callable?

I am relatively new to python, and I started to read the docs when using packages, but I'm having a hard time understanding some of it:
post_to_main_thread(self: open3d.cpu.pybind.visualization.gui.Application, arg0: open3d::visualization::gui::Window, arg1: Callable[[], None]) → None
the only thing here that I don't understand is the arg1 with that callable, and I can't find an explanation on it at the web.
Interesting question!
So post_to_main_thread() is a method that takes 3 arguments (inputs/variables) and returns None.
Because it's a method (a function associated with a class, not just a standalone function) the first argument, self, refers to the instance of the class that the function is part of.
The other two arguments are passed within the function parentheses, as expected with a standalone function. So a call might look like this:
instance_name = open3d.visualization.gui.Application(...)
instance_name.post_to_main_thread(arg1, arg2)
arg1 should be of type open3d::visualization::gui::Window. This is an instance of the class open3d.visualization.gui.Window().
arg2 should be of type Callable(). This describes a number of built-ins that you can find details about in the documentation. To quote:
The subscription syntax must always be used with exactly two values: the argument list and the return type. The argument list must be a list of types or an ellipsis; the return type must be a single type.
So in this case the type should be Callable[[], None], which means this should be a function that takes no input and returns None. Carrying on from our previous example, you'd pass this as an argument like so:
def my_callable:
print('Hello, World!')
return
instance_name.post_to_main_thread(arg1, my_callable)
Does that clear things up?

MethodError calling constructor of parametric struct

I'm trying to create a linked list in Julia.
I have:
mutable struct LLNode{T}
x::T
next::Union{LLNode{T},Void}
prev::Union{LLNode{T},Void}
end
mutable struct LinkedList{T}
count::Int
head::Union{LLNode{T},Void}
tail::Union{LLNode{T},Void}
end
Now, the above code compiles fine. I can also run:x = LLNode(0,nothing,nothing) fine. But when I run y = LinkedList(0,nothing,nothing) I get a no method matching LinkedList(::Int64, ::Void, ::Void) error. What gives?
VERSION returns v"0.6.2"
When you write LLNode(0,nothing,nothing), Julia is able to figure out that it needs to construct an LLNode{Int} based upon the type of the first argument. But in LinkedList(0, nothing, nothing), there's quite literally nothing for it to go on to determine what the type parameter should be, so it doesn't know what to construct.
Instead, you either need to explicitly choose what you want T to be:
julia> LinkedList{Int}(0, nothing, nothing)
LinkedList{Int64}(0, nothing, nothing)
or it can get the T based upon a not-nothing argument:
julia> LinkedList(0, LLNode(0, nothing, nothing), nothing)
LinkedList{Int64}(0, LLNode{Int64}(0, nothing, nothing), nothing)
The reason is that LinkedList requires parameter T. If you pass nothing as second and third argument there is no way for Julia to infer what T is.
Therefore you either have to explicitly specify it, e.g.:
julia> LinkedList{Int}(0, nothing, nothing)
LinkedList{Int64}(0, nothing, nothing)
or pass a second and/or third argument allowing to infer T, e.g. using your x:
julia> LinkedList(0, x, x)
LinkedList{Int64}(0, LLNode{Int64}(0, nothing, nothing), LLNode{Int64}(0, nothing, nothing))
As a side note --- you might want to check out https://github.com/ChrisRackauckas/LinkedLists.jl for an example of a fairly complete implementation of linked list.

Julia: How much can we change the objects in immutable struct type?

I have an immutable structure with four objects defined as follows:
struct FltFric
muS::Array{Float64, 2}
muD::Array{Float64, 2}
Dc::Float64
W::Array{Float64, 2}
end
muS = repmat([0.6], 100, 1) # Coefficient of static friction
muD = repmat([0.5], 100, 1) # Coefficient of dynamic friction
Dc = 0.1 # Critical slip distance
FltFriction = FltFric(muS, muD, Dc, zeros(size(muS)))
I am modifying the values of FltFric.muS as follows:
FltFriction.muS[1:20] = 100
This works fine. But when I try to modify the value of W
FltFriction.W = (FltFriction.muS - FltFriction.muD)./(FltFriction.Dc)
This gives me an error: type FltFric is immutable.
Why does the first statement not give error while the second one does? If the type is immutable, both statements should give an error. What is the difference between the two assignments?
I know that I can circumvent the problem by typing mutable struct, but I don't understand the difference in my two assignments.
I am not a Julia expert, but I think this is a more general question.
In the first assignment, you're modifying certain elements of the list FltFriction.muS. This is fine since although the struct is immutable, the list referred to by .muS is mutable. In other words, you're mutating the list by changing its elements, rather than mutating the struct.
In the second assignment, you're trying to replace the entire list .W in one fell swoop. In this case you're trying to mutate the struct directly, replacing one of its elements. For this reason, the second assignment fails while the first one succeeds.
I'm speculating here, but I suspect that if you tried to do the second assignment like so:
FltFriction.W[1:end] = ...
Then you would be fine, since you're mutating the list instead of the struct.
As pointed out by a commenter (see below), in Julia there is a "more idiomatic (and more performant)" way to do this correctly and without mutating the struct itself by using the in-place assignment operator (neat!):
FltFriction.W .= (FltFriction.muS - FltFriction.muD)./FltFriction.Dc

PyQt_PyObject equivalent when using new-style signals/slots?

So I have a need to pass around a numpy array in my PyQt Application. I first tried using the new-style signals/slots, defining my signal with:
newChunkToProcess = pyqtSignal(np.array()), however this gives the error:
TypeError: Required argument 'object' (pos 1) not found
I have worked out how to do this with the old-style signals and slots using
self.emit(SIGNAL("newChunkToProcess(PyQt_PyObject)"), np.array([5,1,2])) - (yes, that's just testing data :), but I was wondering, is it possible to do this using the new-style system?
The type you're looking for is np.ndarray
You can tell this from the following code:
>>> arr = np.array([]) # create an array instance
>>> type(arr) # ask 'what type is this object?'
<type 'numpy.ndarray'>
So your signal should look more like:
newChunkToProcess = pyqtSignal(np.ndarray)
(Notice I'm passing the type np.ndarray, rather than an array instance as you tried).
If you don't want to worry about the type of the argument, you could instead use:
newChunkToProcess = pyqtSignal(object)
This should let you send any data type at all through the signal.
Also: numpy and Qt do not share any major functionality that I know of. In fact, the two are quite complementary and make a very powerful combination.
You are doing it wrong. You have to pass the data object type: int, str, ... in your case list
Like I am doing:
images = pyqtSignal(int, str);
failed = pyqtSignal(str, str);
finished = pyqtSignal(int)

Resources