How to create this python object to julia

Hi, I have a code that I want to build on Julia, but I don’t know how to start on.

class Point:
def init(self, center=(0,0)):
self.x = center[0]
self.y = center[1]

def print_point(self):
    print("The point is at ({x}, {y})".format(x=self.x, y=self.y))

class Circle:
def init(self, center, radius):
self.center = Point(center)
self.radius = radius

def print_circle(self):
    print("The circle's center is at ({x}, {y}) and a radius of {radius}"
          .format(x=self.center.x, y=self.center.y, radius=self.radius))

class Rectangle:
def init(self, tl, length, height):
#top left corner
self.tl = Point(tl)
self.length = length
self.height = height
#top right corner
self.tr = Point(((tl[0]+length), tl[1]))
#bottom left corner
self.bl = Point((tl[0], (tl[1]-height)))
#bottom right corner
self.br = Point(((tl[0]+length), (tl[1]-height)))

def print_rect(self):
    print("The corners of the rectangle are ({tl_x}, {tl_y}), ({tr_x}, {tr_y}), ({bl_x}, {bl_y}), ({br_x}, {br_y})"
          .format(tl_x=self.tl.x, tl_y=self.tl.y, 
          tr_x=self.tr.x, tr_y=self.tr.y,
          bl_x=self.bl.x, bl_y=self.bl.y,
          br_x=self.br.x, br_y=self.br.y))

def point_in_circle(c, p):
if ((p.x <= c.center.x+c.radius and p.x >= c.center.x-c.radius) and
(p.y <= c.center.y+c.radius and p.y >= c.center.y-c.radius)):
return True
else:
return False

def rect_in_circle(c, r):
if (point_in_circle(c, r.tl) and point_in_circle(c, r.tr) and
point_in_circle(c, r.bl) and point_in_circle(c, r.br)):
return True
else:
return False

def rect_circle_overlap(c, r):
if (point_in_circle(c, r.tl) or point_in_circle(c, r.tr) or
point_in_circle(c, r.bl) or point_in_circle(c, r.br)):
return True
else:
return False

could I get some help? Thank you

It’s a pretty straightforward translation: you make a Julia struct type for each class in Python with appropriate constructors; for each method in Python you have a corresponding method in Julia with the additional freedom that the methods don’t have to live in one class or the other. What have you tried?

1 Like

the thing is I have no experience in Python and I am a really newcomer for Julia so I don’t know where to start

This is a good place to start:

Tutorials (julialang.org)

1 Like
#=julia uses structs, they need to declare the fields and (optionally) the types of those fields.
when not declared, it normally hurts performance, but as a first example, its alright.
there is an extensive example of constructors of Point here: https://docs.julialang.org/en/v1/manual/constructors/#Parametric-Constructors
=#
struct Point
  x
  y
end

#external constructor, check:https://docs.julialang.org/en/v1/manual/constructors/#man-outer-constructor-methods
function Point(center)
  x,y = center
  #x = center[1] #julia uses one-based indexing
  #y = center[2]
  return Point(x,y)
end
#instead of defining a custom print method
#we are going to overload how Point shows in a REPL:

function Base.show(io::IO,::MIME"text/plain",data::Point)
  x,y = data.x, data.y
  println(io,"The point is at ($x,$y)")
end

struct Circle
  center::Point #we mark here that point should be of the point type
  radius
 #internal constructor, check https://docs.julialang.org/en/v1/manual/constructors/#man-inner-constructor-methods
  Circle(center,radius) = new(Point(center),radius)
end
#inline function
"""
  inside(big_thing,small_thing

function to determine if a thing is inside other thing. this is an example of documentation. returns true or false
"""
function inside(circ::Circle,point::Point)
  x0 = circ.center.x
  y0 = circ.center.y
  r = circ.radius
  x1 = point.x
  x2 = point.y
 return (x1 - x0)^2 + (y1 - y0)^2 <= r^2
end

#multiple dispatch, check https://docs.julialang.org/en/v1/manual/methods/
inside(::Point,::Point) = false

function inside(c1::Circle,c2::Circle)
   println("to be implemented")
  return false
end
7 Likes

That’s a good start, I would simplify a few things and to make it more idiomatic and efficient, add some type parameters. First, the type definitions:

struct Point{R<:Real}
  x :: R
  y :: R
end

abstract type ConvexShape{R} end

struct Circle{R<:Real} <: ConvexShape{R}
  center :: Point{R}
  radius :: R
end

struct Rectangle{R<:Real} <: ConvexShape{R}
  left   :: R
  right  :: R
  bottom :: R
  top    :: R
end

Rectangle(p::Point, q::Point) =
  Rectangle(minmax(p.x, q.x)..., minmax(p.y, q.y)...)

# a matrix of the corners of a rectangle
corners(r::Rectangle) = [Point(x, y) for x in (r.left, r.right), y in (r.bottom, r.top)]

Now some geometric computational methods:

dist(p::Point, q::Point) = sqrt((p.x - q.x)^2 + (p.y - q.y)^2)

inside(p::Point, q::Point) = p == q
inside(p::Point, c::Circle) = dist(p, c.center) ≤ c.radius
inside(p::Point, r::Rectangle) =
  r.left ≤ p.x ≤ r.right && r.bottom ≤ p.y ≤ r.top

inside(c::Circle, c′::Circle) =
  c.radius + dist(c.center, c′.center) ≤ c′.radius
inside(c::Circle, r::Rectangle) =
  r.left ≤ c.center.x - c.radius && c.center.x + c.radius ≤ r.right &&
  r.bottom ≤ c.center.y - c.radius && c.center.y + c.radius ≤ r.top

inside(r::Rectangle, c::ConvexShape) =
  all(inside(p, c) for p in corners(r))

const ⊆ = inside

A few points about why I wrote things this way:

  • Type parameters allow structs to be much more efficient and always have matching types for fields
  • These allow you to use whatever real number type you want for the dimensions, e.g. Float64, Int or something slightly exotic like Rational{BigInt}
  • No need to define constructors most of the time—the defaults just work
  • Also no need to provide show methods often, the default is pretty good
  • Since you can put types on arguments, it’s often clearer to use short argument names for math formulas—you can tell that p is a Point and c is a Circle, etc. from the method signature
  • It is conventional in Julia to express “subject verb object” relationships as verb(subject, object); accordingly one should read inside(c::Circle, r::Rectangle) as “c is inside r”; at the end I create as an alias for inside which allows write this using infix operator notation as c ⊆ r which matches mathematical notation.

About the representation and construction of Rectangles:

  • There are many ways to represent and construct this type
  • I like Rectangle(::Point, ::Point) for construction because you don’t have to remember which corner is special or which which order the arguments are in—if you give any two points it constructs the rectangle between them
  • Internal representation doesn’t really matter, but a list of left, right, bottom and top coordinates seems simple and allows easy construction from two points.

The only bit of type system cleverness here:

  • I create an abstract type ConvexShape and make Circle and Rectangle implementations of it
  • Checking if a rectangle is contained in any convex shape can be done by checking if all its corners are inside the convex shape, so we can define a single method

Note that the way these types are defined they work great if you use the same Real type everywhere (e.g. all Int or all Float64), but they start breaking as soon as you try mixing integers and floats for example. If you want to allow mixing types, then you can define these additional mixed type constructors for convenience:

Point(x::Real, y::Real) =
  Point(promote(x, y)...)

Rectangle(left::Real, right::Real, bottom::Real, top::Real) =
  Rectangle(promote(left, right, bottom, top)...)

function Circle(center::Point{R}, radius::S) where {R<:Real, S<:Real}
  T = promote_type(R, S)
  Circle(Point{T}(center.x, center.y), convert(T, radius))
end

Here’a a fun example that ends up using a lot of the above functionality:

julia> o = Point(0, 0) # origin
Point{Int64}(0, 0)

julia> c₁ = Circle(o, 1)
Circle{Int64}(Point{Int64}(0, 0), 1)

julia> c₂ = Circle(o, 2)
Circle{Int64}(Point{Int64}(0, 0), 2)

julia> c₁ ⊆ c₂
true

julia> c₁ ⊆ c₁
true

julia> c₂ ⊆ c₁
false

julia> r = Rectangle(Point(-1, -1), Point(1, 1))
Rectangle{Int64}(-1, 1, -1, 1)

julia> c₁ ⊆ r ⊆ c₂
true
35 Likes

thank you! now I have another problem with this program. It shows errors and the worst part, I don’t know how to test the code. Please help, I’m kinda feel hopeless

struct Time()
time = Time()
time.hour = 3
time.minute = 0
time.second = 0
end

function time_to_int(time)
minutes = time.hour * 60 + time.minute
seconds = minutes * 60 + time.second
return seconds
end

function int_to_time(seconds)
new_time = Time()
minutes, new_time.second = divmod(seconds, 60)
time.hour, time.minute = divmod(minutes, 60)
return time
end

function mul_time(time, multicand)
time_int = time_to_int(time) * multicand
new_time = int_to_time(time_int)
if new_time.hour > 12
new_time.hour = new_time.hour % 12
println(“New time is: %.2d:%.2d:%.2d” % (new_time.hour, new_time.minute, new_time.second))
return new_time
end
end

mul_time(time, 2)

function race_stats(time, distance)
println(“The finish time was %.2d:%.2d:%.2d” % (time.hour, time.minute, time.second))
println(“The distance was %d miles” % (distance))
end
average = mul_time(time, (1.0 / distance))

println("The average is: %.2d:%.2d:%.2d per mile" % (average.hour, average.minute, average.second))

race_stats(time, 3)

You seem to be having issues with basics of Julia syntax. You may want to start with an intro tutorial and make sure you can get that to work and then go from there. Once you’ve got a grip on the basic syntax you can come back to this specific example.

11 Likes

You should probably start by reading the Manual section on composite types and how to create them: Types · The Julia Language

In particular, your syntax is way off. The above definition should be something like

struct Time
    hour::Int
    minute::Int
    second::Int
end
Time() = Time(3, 0, 0)  # default constructor
5 Likes

sorry, I‘m confuse about this sentence. If a struct is as follows:

struct Point
  x :: Int
  y :: Int
end

it can not accept Float64, and in REPL we can not redefinite a struct, so, why we need the new constructor function as follows?

Point(x::Real, y::Real) =
  Point(promote(x, y)...)

What’more , a snippet code in a REPL

julia> struct Point
         x :: Float64
         y :: Float64
       end

julia> Point(1.1, 1.2)
Point(1.1, 1.2)

julia> Point(1, 1.2)
Point(1.0, 1.2)

why Point(1, 1.2) works well? I know in julia promote(1,1.2)
can promote 1 to 1.0, however, why it promotes automatically?

You need it if the struct is parametric:

struct Point{R<:Real}
    x::R
    y::R
end

If the definition simply requires Float64, the default constructor will try to convert every input to Float64, but in the parametric case, it doesn’t know what to convert to, since Int and Float64 are both <:Real.

julia> Point(2.3, 3)
ERROR: MethodError: no method matching Point(::Float64, ::Int64)

So then you specify that you want to promote:

julia> Point(x, y) = Point(promote(x, y)...);

julia> Point(2.3, 3)  # now it works
Point{Float64}(2.3, 3.0)
2 Likes

Get it and thx. So it seems thatjulia need to to more work on type for mutidispatch and do more work on converting than other language?

What makes you say that?

2 Likes

OpenAI API OpenAI provides interesting interface to translate programming language. You should give it a try (even if that’s far from being perfect)

1 Like