`Vector{Bool}` to Int

I’m using PyCall to interact with a python package which returns integers as a `Vector{Bool}`, e.g. `[true, true, false, true, true, true, false, true]`. How can I easily convert this to an integer?

In a previous question it’s shown how to convert from a bool Str to Int

Should be as simple as this:

``````julia> v = [true, true, false, true, true, true, false, true]
8-element Vector{Bool}:
1
1
0
1
1
1
0
1

julia> Int.(v)
8-element Vector{Int64}:
1
1
0
1
1
1
0
1
``````

Sorry perhaps it wasn’t clear. I’d like to interpret it as

``````parse(Int, "11011101"; base=2)
``````

So the initial example gives `221`

It’s a bit esoteric, but this works:

``````julia> only(BitVector(reverse(v)).chunks) |> Int
221
``````

There may be a better way.

1 Like

Slightly esoteric but it does work, unless someone has a clearer solution I’ll stick with this. Thank you.

You can just use the `String` method in your OP if you construct the string:

``````julia> parse(Int, join(Int.(v), ""), base = 2)
221
``````

maybe less esoteric, but probably less efficient than the current solution.

1 Like

Less esoteric but still fast:

``````julia> evalpoly(2, reverse(v))
221
``````

More esoteric and faster yet:

``````julia> mapreduce(((i, v),) -> (v << (i - 1)), |, enumerate(Iterators.reverse(v)))
221
``````
6 Likes

I think the `evalpoly` is the cleanest and not so slow

1 Like

By modifying the base function so that you don’t have to reverse v, you gain performance

``````
function _evalpoly(x, p)
N = length(p)
ex = p[1]
for i in 2:N
ex *= x
ex+=p[i]
end
ex
end
``````

In my opinion the cleanest, and 4x faster than the fastest `mapreduce` solution above,:

``````reduce((acc, b) -> acc << 1 + b, v; init=0)
``````

Its just 8 bitshifts and 8 additions and is pretty self explanatory, we just shift the `Int` accumulator left and add the next `Bool` as the lowest bit

4 Likes

really nice, but not the fastest

``````julia> using BenchmarkTools

julia> function polyeval(x, p)
N = length(p)
ex = p[1]
for i in 2:N
ex *= x
ex+=p[i]
end
ex
end
polyeval (generic function with 1 method)

julia> @btime polyeval(2,v)
16.533 ns (0 allocations: 0 bytes)
221

julia> @btime reduce((x,y)->x<<1+y, v; init=0)
70.902 ns (0 allocations: 0 bytes)
221

julia> @btime evalpoly(2, reverse(v))
55.589 ns (1 allocation: 64 bytes)
221

julia> @btime mapreduce(((i, v),) -> (v << (i - 1)), |, enumerate(Iterators.reverse(v)))
150.181 ns (3 allocations: 48 bytes)
221

julia> @btime only(BitVector(reverse(v)).chunks) |> Int
316.949 ns (3 allocations: 160 bytes)
221

``````

The base for loop performs even better if it specializes in the specific case of base 2 numbers

``````julia> function poly_2_eval(p)
ex = 0
for e in p
ex *= 2
ex+=e
end
ex
end
poly_2_eval (generic function with 1 method)

julia> @btime poly_2_eval(v)
15.030 ns (0 allocations: 0 bytes)
221
``````

It’s fun to play the benchmarks game, but let’s step back here and remember the context — this `Vector{Bool}` is coming from a Python function. Performance of a linear-time conversion is unlikely to matter in this context, so I would just go with any solution that is clear and requires little code, e.g. `evalpoly(2, reverse(v))`.

4 Likes

@rocco_sprmnt21 Its not the fastest because you are not using `\$` in your benchmarks, so most of the time is in how the type instability is handled, in `polyeval` it just quickly hits a function barrier.

Here with `\$` interpolation:

``````julia> @btime polyeval(2,\$v)
7.856 ns (0 allocations: 0 bytes)
221

julia> @btime reduce((x,y)->x<<1+y, \$v; init=0)
3.825 ns (0 allocations: 0 bytes)
221

julia> @btime evalpoly(2, reverse(\$v))
43.112 ns (1 allocation: 64 bytes)
221

julia> @btime mapreduce(((i, v),) -> (v << (i - 1)), |, enumerate(Iterators.reverse(\$v)))
13.070 ns (0 allocations: 0 bytes)
221

julia> @btime only(BitVector(reverse(\$v)).chunks) |> Int
63.108 ns (3 allocations: 160 bytes)
221
``````

@stevengj I mostly posted my solution for the simplicity. `evalpoly` is less clear to me than `<<` because I have to think more about about what `evalpoly` is doing behind the scenes - it’s not a function I use much, where as `reduce` and `<<` are generic. Of course that will vary person to person.

2 Likes

The loop I proposed is exactly the one (with a few modifications) with which the evalpoly function is built.
I did not know of this function (and I think it is not very well known in general).
This is why I believe that @Raf’s solution with reduce is preferable.
On the other hand, I believe that the two functions reduce (…) and poly_2_eval are, in a sense, isomorphic.
They probably have the same lowered code.
In fact, in terms of performance they are practically the same …

``````julia> v = [true, true, false, true, true, true, false, true]
8-element Vector{Bool}:
1
1
0
1
1
1
0
1

julia> function poly_2_eval(p)
ex = 0
for e in p
ex *= 2
ex+=e
end
ex
end
poly_2_eval (generic function with 1 method)

julia> @btime reduce((x,y)->x<<1+y, \$v; init=0)
4.600 ns (0 allocations: 0 bytes)
221

julia> @btime reduce((x,y)->x*2+y, \$v; init=0)
4.600 ns (0 allocations: 0 bytes)
221

julia> @btime poly_2_eval(\$v)
4.200 ns (0 allocations: 0 bytes)
221
``````