1

I am doing monte-carlo-integration of a rectangle that contains rectangle/s inside the enclosing rectangle. I want to find out the area of rectangle that is not covered by any of embedded rectangle.

enter image description here

Below are the class of rectangle and the montecarlo integration. I need help in checking whether a point is inside a given rectangle (def inside(...)). I get the error

File "/skeleton_assignment01_example1/example1/montecarlo.py", line 67, in inside
    number_recs = len(rect)
TypeError: 'method' object cannot be interpreted as an integer

I don't know how to interpret this and not very familiar with working with objects. Maybe there is a different way to do it or my approach is wrong. Thanks


class Rectangle():

    def __init__(self, origin_x, origin_y, length, width):
        self.origin_x = origin_x
        self.origin_y = origin_y
        self.length = length
        self.width = width
    
    def rect_values(self):
        return self.origin_x, self.origin_y, self.length, self.width

from random import random


class MonteCarlo:

    def __init__(self, length, width, rectangles):
       
        rect_specs = length or width or rectangles
        
        if isinstance(rect_specs, type(None)):
            raise ValueError
        
        self.rec_length = length
        self.rec_width = width
        self.rects = rectangles
        
        
    
    def area(self, num_of_shots):
        """Method to estimate the area of the enclosing rectangle that is not covered by the embedded rectangles
        """
    
        if isinstance(num_of_shots, type(None)):
            raise ValueError
            
        inside = 0
        total = num_of_shots
        
        for i in range(num_of_shots):
            
            x = random() * self.rec_length
            y = random() * self.rec_width
            
            if self.inside(x,y, self.rects) == True:
                inside += 1
        area = (total - inside) / (total * self.rec_length * self.rec_width)
        return area
    
        
    
    
    def inside(self, x, y, rect):
        """Method to determine if a given point (x,y) is inside a given rectangle
        """
        if (x or y or rect) == None:
            raise ValueError

        number_recs = len(rect)
        for i in range(number_recs):
            origin_x, origin_y, length, width = rect[i].rect_values()
            
            x_line = length+origin_x
            y_line = width+origin_y

            if (x <= x_line and y <= y_line) and (x >= origin_x and y >= origin_y):
                return True

Here is the long unittest that throws the error when running! Hope this helps.

import unittest

from montecarlo import MonteCarlo
from rectangle import Rectangle

class Point():
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def get_x(self):
        return self.x

    def get_y(self):
        return self.y


class TestAssignment01ex01Student(unittest.TestCase):
    def testMCwith3rects(self):
        rect1 = Rectangle(0.0, 0.0, 4.0, 4.0)
        rect2 = Rectangle(6.0, 3.0, 6.0, 4.0)
        rect3 = Rectangle(10.0, 5.0, 4.0, 4.0)
        rects = [rect1, rect2, rect3]

        mc = MonteCarlo(15.0, 10.0, rects)

        area = mc.area(1000000)
        self.assertTrue((area >= 97.5 and area <= 98.5),
                        "MonteCarlo.area(1000000) with the enclosing rectangle 15.0x10.0 " +
                        "and the following embedded rectangles failed (area should be +/- 0.5 to the result 98 but your result is: " + str(
                            area) + " " + self.printRectanglesData(rects))

    def testMCwith2Rects(self):
        # area of enclosing rectangle is 50
        # area of embedded rectangles is 2+12=14
        # result is: 50-14=36
        #
        # embedded rectangle 1 is at position (0,0) with a size of 1x2
        rect1 = Rectangle(0.0, 0.0, 1.0, 2.0)
        # embedded rectangle 2 is at position (7,1) with a size of 3x4
        rect2 = Rectangle(7.0, 1.0, 3.0, 4.0)
        rects = [rect1, rect2]

        mc = MonteCarlo(10.0, 5.0, rects)

        area = mc.area(10000)
        # for 10k random points the estimated area should already be close to correct result of 36
        self.assertTrue((area > 30 and area < 40),
                        "MonteCarlo.area() according to the provided unit test failed! Area should be between 30 and 40 but was " + str(
                            area))

    def testInsideRectBorderline1Y(self):
        rect1 = Rectangle(0.0, 0.0, 4.0, 4.0)
        rects = []

        mc = MonteCarlo(15.0, 10.0, rects)

        pt = Point(1.0, 4.0)

        self.assertTrue(mc.inside(pt.get_x(), pt.get_y(), rect1),
                        "MonteCarlo.inside() with point (x=" + str(pt.get_x()) + "/y=" + str(
                            pt.get_y()) + ") and the rectangle "
                        + self.printRectangleData(rect1) + " returned False but should be True!")

    def testInsideRectBorderline2Y(self):
        rect1 = Rectangle(0.0, 0.0, 4.0, 4.0)
        rects = []

        mc = MonteCarlo(15.0, 10.0, rects)

        pt = Point(1.0, 0.0)

        self.assertTrue(mc.inside(pt.get_x(), pt.get_y(), rect1),
                        "MonteCarlo.inside() with point (x=" + str(pt.get_x()) + "/y=" + str(
                            pt.get_y()) + ") and the rectangle "
                        + self.printRectangleData(rect1) + " returned False but should be True!")

    def testInsideRectBorderline3Y(self):
        rect1 = Rectangle(0.0, 0.0, 4.0, 4.0)
        rects = []

        mc = MonteCarlo(15.0, 10.0, rects)

        pt = Point(1.0, 4.01)

        self.assertFalse(mc.inside(pt.get_x(), pt.get_y(), rect1),
                         "MonteCarlo.inside() with point (x=" + str(pt.get_x()) + "/y=" + str(
                             pt.get_y()) + ") and the rectangle "
                         + self.printRectangleData(rect1) + " returned True but should be False!")

    def testInsideRectBorderline4Y(self):
        rect1 = Rectangle(0.0, 0.0, 4.0, 4.0)
        rects = []

        mc = MonteCarlo(15.0, 10.0, rects)

        pt = Point(1.0, -0.01)

        self.assertFalse(mc.inside(pt.get_x(), pt.get_y(), rect1),
                         "MonteCarlo.inside() with point (x=" + str(pt.get_x()) + "/y=" + str(
                             pt.get_y()) + ") and the rectangle "
                         + self.printRectangleData(rect1) + " returned True but should be False!")

    def testInsideRectBorderline1X(self):
        rect1 = Rectangle(0.0, 0.0, 4.0, 4.0)
        rects = []

        mc = MonteCarlo(15.0, 10.0, rects)

        pt = Point(0.0, 3.0)

        self.assertTrue(mc.inside(pt.get_x(), pt.get_y(), rect1),
                        "MonteCarlo.inside() with point (x=" + str(pt.get_x()) + "/y=" + str(
                            pt.get_y()) + ") and the rectangle "
                        + self.printRectangleData(rect1) + " returned False but should be True!")

    def testInsideRectBorderline2X(self):
        rect1 = Rectangle(0.0, 0.0, 4.0, 4.0)
        rects = []

        mc = MonteCarlo(15.0, 10.0, rects)

        pt = Point(4.0, 3.0)

        self.assertTrue(mc.inside(pt.get_x(), pt.get_y(), rect1),
                        "MonteCarlo.inside() with point (x=" + str(pt.get_x()) + "/y=" + str(
                            pt.get_y()) + ") and the rectangle "
                        + self.printRectangleData(rect1) + " returned False but should be True!")


   
    # /*************************************
    #  *
    #  * private methods
    #  *
    #  */

    def printRectanglesData(self, rects):
        i = 0
        sb = ""
        while i < len(rects):
            sb += "\n{} Rectangle ".format(i + 1) + "(x="
            sb += str(rects[i].origin_x) + " y=" + str(rects[i].origin_y)
            sb += " length="
            sb += str(rects[i].length) + " width=" + str(rects[i].width) + ")"
            i += 1
        return sb

    def printRectangleData(self, rect):
        sb = " (x="
        sb += str(rect.origin_x) + " y=" + str(rect.origin_y)
        sb += " length="
        sb += str(rect.length) + " width=" + str(rect.width) + ")"

        return sb

if __name__ == '__main__':
    unittest.main()
DarrylG
  • 16,732
  • 2
  • 17
  • 23
Gunners
  • 55
  • 5
  • 2
    what do you expect `Rectangle.__len__` to do? At the moment the method is just returning itself, and as the error says, `len()` requires an integer to be returned, but you are returning a method – Phydeaux Oct 26 '21 at 12:23
  • 1
    Stackoverflow encourages providing a [Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) of your code an the error. The provided code does not cause the error (i.e. you should add example with the classes being used). – DarrylG Oct 26 '21 at 13:01
  • Thanks DarrylG for pointing out! – Gunners Oct 26 '21 at 14:11
  • Phydeaux, Rectangle.__len__ was just a crap I forgot to delete, sorry for that – Gunners Oct 26 '21 at 14:33
  • @Gunners -- stackoverflow encourages **minimal reproducible example**. This is because the more code you have the less likely you are to get an answer from readers. Two suggestions 1) minimize the code (i.e. don't include code not necessary to illustrate the problem), and 2) the code should be properly fromatted. – DarrylG Oct 26 '21 at 14:35
  • @DarrylG Thanks. I just edited it according to your suggestions. Hope this is okay. – Gunners Oct 26 '21 at 15:03
  • @Gunners -- 1) in `Class Rectangle`, method `inside` is to check if a point x, y is inside a rectangle (i.e. single rectangle). Why do you have `number_recs = len(rect)`, when rect is just a single rectangle. 2) `(x or y or rect) == None` is not the way to check if x is None or y is None or rect is None. can use: `if None in [x, y, rect]:`. – DarrylG Oct 26 '21 at 15:58
  • @DarrylG Thanks a lot for me helping out. I also liked your suggestions for checking if nothing has the value None. – Gunners Oct 26 '21 at 19:45
  • @Gunners -- glad suggestions help. But what about my answer? – DarrylG Oct 26 '21 at 20:07
  • @DarrylG I now get that number_recs doesn't make sense if I want to give a single rectangle. But how would you implement it if you wanted to give multiple rectangles? This is where I am also struggling. I am sorry for not having enough reputation to accept the answer. – Gunners Oct 26 '21 at 20:09
  • @Gunners -- modified method inside so it now works with one or more rectangles using argument unpacking i.e. `inside(x, y, rect1, rect2, rect3, ...rectN)`. Let me know if this doesn't make sense. – DarrylG Oct 26 '21 at 20:28
  • @DarrylG Thanks for the work. So you used unpacking of lists for it, which didn't come to my mind. – Gunners Oct 28 '21 at 06:04
  • @Gunners - yes, by using unpacking we can handle 1 or more rectangles, rather than having to handle two cases: 1) single rectangle, 2) list of rectangles. – DarrylG Oct 28 '21 at 07:49

1 Answers1

0

Main issue:

  • Method inside when used by method area expects a list of rectangles
  • Method inside when used inside unittests such as testInsideRectBorderline1Y, expects a single rectange.
  • Modified method inside so it takes one or more rectangles

Code

File rectangle.py

class Rectangle():

    def __init__(self, origin_x, origin_y, length, width):
        self.origin_x = origin_x
        self.origin_y = origin_y
        self.length = length
        self.width = width
    
    def rect_values(self):
        return self.origin_x, self.origin_y, self.length, self.width
    

File montecarlo.py

from random import random

class MonteCarlo:

    def __init__(self, length, width, rectangles):
       
        if None in (length, width, rectangles):
            raise ValueError
        
        self.rec_length = length
        self.rec_width = width
        self.rects = rectangles
        
        
    
    def area(self, num_of_shots):
        """Method to estimate the area of the enclosing rectangle that is not covered by the embedded rectangles
        """
    
        if isinstance(num_of_shots, type(None)):
            raise ValueError
            
        inside = 0
        total = num_of_shots
        
        for i in range(num_of_shots):
            x = random() * self.rec_length
            y = random() * self.rec_width
            
            if self.inside(x, y, *self.rects):  # Use list unpacking into arguments
                                                # https://stackoverflow.com/questions/7527849/how-to-extract-parameters-from-a-list-and-pass-them-to-a-function-call
                inside += 1
                
        area = (total - inside) / (total * self.rec_length * self.rec_width)
        return area
    
    
    def inside(self, x, y, *rects):
        """Method to determine if a given point (x,y) is inside one or more rectangles
        """
        if None in (x, y, rects):
            raise ValueError

        for rect in rects:
            origin_x, origin_y, length, width = rect.rect_values()

            x_line = length+origin_x
            y_line = width+origin_y

            # Simplify conditionals and return boolean
            if origin_x <= x <= x_line and origin_y <= y < y_line:
                return True
            
        return False
    

File main.py

import unittest

from montecarlo import MonteCarlo
from rectangle import Rectangle

class Point():
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def get_x(self):
        return self.x

    def get_y(self):
        return self.y


class TestAssignment01ex01Student(unittest.TestCase):
    def testMCwith3rects(self):
        rect1 = Rectangle(0.0, 0.0, 4.0, 4.0)
        rect2 = Rectangle(6.0, 3.0, 6.0, 4.0)
        rect3 = Rectangle(10.0, 5.0, 4.0, 4.0)
        rects = [rect1, rect2, rect3]

        mc = MonteCarlo(15.0, 10.0, rects)

        area = mc.area(1000000)
        self.assertTrue((area >= 97.5 and area <= 98.5),
                        "MonteCarlo.area(1000000) with the enclosing rectangle 15.0x10.0 " +
                        "and the following embedded rectangles failed (area should be +/- 0.5 to the result 98 but your result is: " + str(
                            area) + " " + self.printRectanglesData(rects))

    def testMCwith2Rects(self):
        # area of enclosing rectangle is 50
        # area of embedded rectangles is 2+12=14
        # result is: 50-14=36
        #
        # embedded rectangle 1 is at position (0,0) with a size of 1x2
        rect1 = Rectangle(0.0, 0.0, 1.0, 2.0)
        # embedded rectangle 2 is at position (7,1) with a size of 3x4
        rect2 = Rectangle(7.0, 1.0, 3.0, 4.0)
        rects = [rect1, rect2]

        mc = MonteCarlo(10.0, 5.0, rects)

        area = mc.area(10000)
        # for 10k random points the estimated area should already be close to correct result of 36
        self.assertTrue((area > 30 and area < 40),
                        "MonteCarlo.area() according to the provided unit test failed! Area should be between 30 and 40 but was " + str(
                            area))

    def testInsideRectBorderline1Y(self):
        rect1 = Rectangle(0.0, 0.0, 4.0, 4.0)
        rects = []

        mc = MonteCarlo(15.0, 10.0, rects)

        pt = Point(1.0, 4.0)

        self.assertTrue(mc.inside(pt.get_x(), pt.get_y(), rect1),
                        "MonteCarlo.inside() with point (x=" + str(pt.get_x()) + "/y=" + str(
                            pt.get_y()) + ") and the rectangle "
                        + self.printRectangleData(rect1) + " returned False but should be True!")

    def testInsideRectBorderline2Y(self):
        rect1 = Rectangle(0.0, 0.0, 4.0, 4.0)
        rects = []

        mc = MonteCarlo(15.0, 10.0, rects)

        pt = Point(1.0, 0.0)

        self.assertTrue(mc.inside(pt.get_x(), pt.get_y(), rect1),
                        "MonteCarlo.inside() with point (x=" + str(pt.get_x()) + "/y=" + str(
                            pt.get_y()) + ") and the rectangle "
                        + self.printRectangleData(rect1) + " returned False but should be True!")

    def testInsideRectBorderline3Y(self):
        rect1 = Rectangle(0.0, 0.0, 4.0, 4.0)
        rects = []

        mc = MonteCarlo(15.0, 10.0, rects)

        pt = Point(1.0, 4.01)

        self.assertFalse(mc.inside(pt.get_x(), pt.get_y(), rect1),
                         "MonteCarlo.inside() with point (x=" + str(pt.get_x()) + "/y=" + str(
                             pt.get_y()) + ") and the rectangle "
                         + self.printRectangleData(rect1) + " returned True but should be False!")

    def testInsideRectBorderline4Y(self):
        rect1 = Rectangle(0.0, 0.0, 4.0, 4.0)
        rects = []

        mc = MonteCarlo(15.0, 10.0, rects)

        pt = Point(1.0, -0.01)

        self.assertFalse(mc.inside(pt.get_x(), pt.get_y(), rect1),
                         "MonteCarlo.inside() with point (x=" + str(pt.get_x()) + "/y=" + str(
                             pt.get_y()) + ") and the rectangle "
                         + self.printRectangleData(rect1) + " returned True but should be False!")

    def testInsideRectBorderline1X(self):
        rect1 = Rectangle(0.0, 0.0, 4.0, 4.0)
        rects = []

        mc = MonteCarlo(15.0, 10.0, rects)

        pt = Point(0.0, 3.0)

        self.assertTrue(mc.inside(pt.get_x(), pt.get_y(), rect1),
                        "MonteCarlo.inside() with point (x=" + str(pt.get_x()) + "/y=" + str(
                            pt.get_y()) + ") and the rectangle "
                        + self.printRectangleData(rect1) + " returned False but should be True!")

    def testInsideRectBorderline2X(self):
        rect1 = Rectangle(0.0, 0.0, 4.0, 4.0)
        rects = []

        mc = MonteCarlo(15.0, 10.0, rects)

        pt = Point(4.0, 3.0)

        self.assertTrue(mc.inside(pt.get_x(), pt.get_y(), rect1),
                        "MonteCarlo.inside() with point (x=" + str(pt.get_x()) + "/y=" + str(
                            pt.get_y()) + ") and the rectangle "
                        + self.printRectangleData(rect1) + " returned False but should be True!")


   
    # /*************************************
    #  *
    #  * private methods
    #  *
    #  */

    def printRectanglesData(self, rects):
        i = 0
        sb = ""
        while i < len(rects):
            sb += "\n{} Rectangle ".format(i + 1) + "(x="
            sb += str(rects[i].origin_x) + " y=" + str(rects[i].origin_y)
            sb += " length="
            sb += str(rects[i].length) + " width=" + str(rects[i].width) + ")"
            i += 1
        return sb

    def printRectangleData(self, rect):
        sb = " (x="
        sb += str(rect.origin_x) + " y=" + str(rect.origin_y)
        sb += " length="
        sb += str(rect.length) + " width=" + str(rect.width) + ")"

        return sb

if __name__ == '__main__':
    unittest.main()
    # Note: use line below instead with Jupyter Notebook
    # unittest.main(argv=['first-arg-is-ignored'], exit=False)
DarrylG
  • 16,732
  • 2
  • 17
  • 23