How can I generate an object from a mathematical function?
Have used the equations found here http://www.econym.demon.co.uk/isotut/real.htm#heart1
The first can be crunched into XYZ surface.  Notice the use of fabs(...) in the A helper function.  The abs(...) method is not in the list of allowable methods and creates an error.

Here is some code for the second. Which required a mapping from spherical coordinates.  Run the code to produce a heart mesh.  Change the line h = heart(1.0, 1.0, 0.2) to produce other hearts.
import bpy
import bmesh
from math import degrees, radians, sin, cos, tan
from mathutils import Vector
from bpy import context

class SphericalCoordsPoint:
    @property
    def xyz(self):
        theta = self.theta
        zi = self.zi
        x = cos(theta) * sin(zi)
        y = sin(theta) * sin(zi)
        z = cos(zi)
        R = self.R
        return R * Vector((x,y,z))

    def __init__(self, R, theta, zi):
        self.R = R
        self.theta = theta
        self.zi = zi
        #self.xyz = self.point(theta, zi)

    def __repr__(self):
        return &quot;SphericalCoord(%.4f, %.4f)&quot; % (degrees(self.theta),
                                               degrees(self.zi))
# define the heart method.
def heart(a, b, c):
    def heart(v):
        x = a * v.x
        y = b * (v.y -pow(abs(v.x),0.8))
        z = c * (v.z)
        return Vector((x, y, z))
    return heart

# make the heart bmesh
bm = bmesh.new()

# TODO come up with a nicer way to do this.
sphere_rings = [[SphericalCoordsPoint(1, radians(theta), radians(zi)) 
                 for theta in range (0, 360, 10)]
                 for zi in range(0, 181, 10)]

h = heart(1.0, 1.0, 0.2)

verts0 = [bm.verts.new(h(p.xyz)) for p in sphere_rings[0]]
verts0.append(verts0[0])
for ring in range(1, len(sphere_rings)):

    verts1 = [bm.verts.new(h(p.xyz)) for p in sphere_rings[ring]]
    verts1.append(verts1[0])

    faces = [
        bm.faces.new((
            verts0[i], verts1[i],
            verts1[i+1], verts0[i+1]
        ))
        for i in range(len(verts0) - 1)
    ]
    verts0 = verts1

# create mesh link it to scene etc
mesh = bpy.data.meshes.new(&quot;heart&quot;)
bm.to_mesh(mesh)
obj = bpy.data.objects.new(&quot;heart&quot;, mesh)
scene = context.scene

context.collection.objects.link(obj)
context.view_layer.objects.active = obj
obj.select_set(True)
obj.location = scene.cursor.location

which produced this result filled like the UV sphere (the node is in the middle of the heart

Can change the shape by tweaking the scalars in the heart equation. For example
def heart(v):
    x = v.x
    y = v.y -pow(abs(v.x),0.8)*1
    z = v.z * 0.5
    return Vector((x, y, z))

EDIT: this can now be done with h = heart(1.0, 1.0, 0.5)
produces

Samples of both using Spherical and Toroidal spaces

Another similarish object is the cardioid