# Reducing set of cartesian coordinates due to symmetry

**URL:** <https://discourse.julialang.org/t/reducing-set-of-cartesian-coordinates-due-to-symmetry/86783>\
**Category:** New to Julia\
**Created:** [September 5, 2022, 7:05am UTC](https://discourse.julialang.org/t/reducing-set-of-cartesian-coordinates-due-to-symmetry/86783 "2022-09-05T07:05:19Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![blackeneth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/blackeneth/32/10353_2.png) [@blackeneth](https://discourse.julialang.org/u/blackeneth)\
**Post date:** [September 5, 2022, 7:05am UTC](https://discourse.julialang.org/t/reducing-set-of-cartesian-coordinates-due-to-symmetry/86783/1 "2022-09-05T07:05:19Z")

</div>

**My Question:**  
I have written a Julia function that does this reduction (see below). It works.

However, I have a feeling, that this being Julia, that there is an easier, cleverer, or more efficient method to reduce this set based on symmetry.

**Background:**  
Coordinates on a cartesian plane may exhibit several symmetries to each other: y-axis symmetry, x-axis symmetry, origin symmetry, symmetry over y=x, and symmetry over y=-x.

The problem is: given a set of coordinates, reduce the set by taking out any point that is symmetric to another point.

If a point has coordinates (r,c), then the set of symmetric points is (r,-c), (-r,c), (c,r), (-c,r), (-r,-c), (c,-r), (-c,r)

**Example:**  
The set of points (3,2), (2,3), (3, 4), (4,3)

First center the points around (0,0) by subtracting (3,3) from each point

resulting set: (0,-1), (-1, 0), (0, 1), (1,0)

This set has maximum symmetry – each point is symmetric to some other point.

The correct result is then any of the points from the set.

My function will return (0,1) – or (3,4) before centering.

**Overview of Function:**  
It uses scoreboarding to mark points that are determined to be symmetric with any other point, then reduces the set to those points that weren’t identified to be symmetric to any other point.

In the above example, the scoreboard ends up with these values:

```julia
Dict{point, Int64} with 4 entries:
  point(0, 1) => 0
  point(1, 0) => 1
  point(-1, 0) => 1
  point(0, -1) => 1

```

The points with values 0 are chosen; in this case, point(0,1). When the offsets are added back, it becomes point(3,4)

**The Function:**

```julia
struct point
    r::Int32
    c::Int32
end

function symreduce(ys::Set,offsetr::Integer,offsetc::Integer)

    if length(ys) > 1

        # transform points by subtracting offset from r,c
        coord_set = Set([])
        for pt in ys
            newpt = Set([point(pt.r-offsetr,pt.c-offsetc)])
            union!(coord_set,newpt)
        end

        ptc = collect(coord_set)

        sym_score = Dict{point,Int32}()
        for pt in ptc
            sym_score[pt]=0
        end 

        for i in 1:length(ptc)
            r = ptc[i].r
            c = ptc[i].c 
            check_pt = Set([point(r,c)])
            sym_set = Set([point(r,-c),point(-r,c),point(c,r),point(-c,-r),point(-r,-c),point(c,-r),point(-c,r)])
            for j in i+1:length(ptc)
                test_pt = Set([ptc[j]])
                if length(union(test_pt,sym_set))==length(sym_set)
                    sym_score[ptc[j]] = 1
                end
            end
        end
        
        final_set = findall(x->x==0,sym_score)

    else
        return(ys)
    end 
    
    # add offset back to final set
    return_set = Set([])
    for pt in final_set
        newpt = Set([point(pt.r+offsetr,pt.c+offsetc)])
        union!(return_set,newpt)
    end

    return(return_set)

end 

```

Examples of using the function:

```julia
julia> my_set1 = Set([point(3,2),point(2,3), point(4,3), point(3,4)])
Set{point} with 4 elements:
  point(3, 2)
  point(2, 3)
  point(3, 4)
  point(4, 3)

julia> symreduce(my_set1,3,3)
Set{Any} with 1 element:
  point(3, 4)

julia> my_set2 = Set([point(7,9), point(9,9), point(11,13), point(10,13), point(11,13), point(12,12)])
Set{point} with 5 elements:
  point(12, 12)
  point(11, 13)
  point(10, 13)
  point(9, 9)
  point(7, 9)

julia> symreduce(my_set2,8,7)
Set{Any} with 4 elements:
  point(12, 12)
  point(11, 13)
  point(10, 13)
  point(7, 9)

```

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [September 5, 2022, 7:44am UTC](https://discourse.julialang.org/t/reducing-set-of-cartesian-coordinates-due-to-symmetry/86783/2 "2022-09-05T07:44:23Z")

</div>

First of all, you could use `StaticArrays` to make your struct into a vector like object. (By the way, in Julia it would be more common to use capitalised names for types, i.e. `Point` instead of `point`.)  
Then you could define a way how to uniquely identify each point to an equivalent point.

```julia
using StaticArrays

struct point <: FieldVector{2, Int32}
    r::Int32
    c::Int32
end

function unique_rep(p)
    r, c = p.r, p.c 
    return point(min( (r,c), (r,-c), (-r,c), (c,r), 
                      (-c,r), (-r,-c), (c,-r), (-c,r)))
end

function symreduce(ys, offset = point(0,0))
    unique_pts = []

    for pt in ys 
        unique_pt = unique_rep(pt - offset) + offset
        push!(unique_pts, unique_pt) 
    end

    return Set(unique_pts)
end

my_pts1 = [point(3,2),point(2,3), point(4,3), point(3,4)]
symreduce(my_pts1, point(3,3)) # == point(2, 3)

```

---

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [September 5, 2022, 7:49am UTC](https://discourse.julialang.org/t/reducing-set-of-cartesian-coordinates-due-to-symmetry/86783/3 "2022-09-05T07:49:44Z")

</div>

From a type-stability point of view, the `symreduce` function is better written as

```julia
julia> function symreduce2(ys, offset = point(0,0))
           unique_pts = map(ys) do pt 
                unique_rep(pt - offset) + offset
           end

           return Set(unique_pts)
       end
symreduce2 (generic function with 2 methods)

julia> symreduce(my_pts1, point(3,3))
Set{Any} with 1 element:
  Int32[2, 3]

julia> symreduce2(my_pts1, point(3,3))
Set{point} with 1 element:
  Int32[2, 3]

```

---

<div class="post-metadata">

**Author:** ![blackeneth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/blackeneth/32/10353_2.png) [@blackeneth](https://discourse.julialang.org/u/blackeneth)\
**Post date:** [September 6, 2022, 5:10am UTC](https://discourse.julialang.org/t/reducing-set-of-cartesian-coordinates-due-to-symmetry/86783/4 "2022-09-06T05:10:35Z")

</div>

Thank you both for your answers. I learned something from each!

---

<div class="post-metadata">

**Author:** ![digital\_carver](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/digital_carver/32/33818_2.png) [@digital\_carver](https://discourse.julialang.org/u/digital_carver)\
**Post date:** [September 6, 2022, 7:55pm UTC](https://discourse.julialang.org/t/reducing-set-of-cartesian-coordinates-due-to-symmetry/86783/5 "2022-09-06T19:55:26Z")

</div>

There’s an inbuilt `CartesianIndex` type for storing cartesian (integer) coordinates, which you can use here as well.

```julia
const CI = CartesianIndex # just an alias to make the type name shorter

function unique_rep(p)
  r, c = Tuple(p)
  r, c = min(r, -r), min(c, -c)
  return CI(min((r, c), (c, r)))
end

function symreduce(ys, offset = CI(0,0))
  unique_pts = Set{CI}()

  for pt in ys 
    unique_pt = unique_rep(pt - offset) + offset
    push!(unique_pts, unique_pt) 
  end

  return unique_pts
end

function main()
  my_pts1 = [CI(3,2),CI(2,3), CI(4,3), CI(3,4)]
  symreduce(my_pts1, CI(3,3)) # == CI(2, 3)
end

```

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [September 6, 2022, 8:31pm UTC](https://discourse.julialang.org/t/reducing-set-of-cartesian-coordinates-due-to-symmetry/86783/6 "2022-09-06T20:31:47Z")

</div>

> [@digital\_carver](#):
>
> ```julia
> function unique_rep(p)
> r, c = Tuple(p)
> r, c = min(r, -r), min(c, -c)
> return CI(min((r, c), (c, r)))
> end
> 
> ```

That is super nice 🙂 so short and elegant!
