Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalid layer weight: '10.0'. Expected type: <class 'int'> #56

Closed
crush0x opened this issue Oct 1, 2023 · 7 comments · Fixed by #57
Closed

Invalid layer weight: '10.0'. Expected type: <class 'int'> #56

crush0x opened this issue Oct 1, 2023 · 7 comments · Fixed by #57

Comments

@crush0x
Copy link

crush0x commented Oct 1, 2023

Hi,

I've just started playing around with nft-generator-py, great work! I'm currently running into an error when trying to validate my config file, which is as the title of the issue suggests: "Invalid layer weight: '10.0'. Expected type: <class 'int'>"

All of the weights are being generated as floating point, even if there are only 5 traits in a particular folder (e.g. weights are then:
[20.0, 20.0, 20.0, 20.0, 20.0], instead of [20, 20, 20, 20, 20], and the validation stage seems to consider this an error:

raise ConfigValidationError(
src.common.exceptions.ConfigValidationError: config["layers"][0]["weights"][0]: Invalid layer weight: '10.0'. Expected type: <class 'int'>

Is there any known way to avoid this issue?

@Jon-Becker
Copy link
Owner

This should be covered by the above PR, i'll take a look and make a release.

@crush0x
Copy link
Author

crush0x commented Oct 2, 2023

This should be covered by the above PR, i'll take a look and make a release.

nice one, I'll check that out cheers

@crush0x
Copy link
Author

crush0x commented Oct 3, 2023

just tried adding that line weight.append(int((100 / len(item_list[i])))) into config.py, and it gives me a ZeroDivisionError btw

@crush0x
Copy link
Author

crush0x commented Oct 4, 2023

I believe the ZeroDivisionError is happening because with some trait folders in my collection there are for example 150 traits (I can probably solve this by further separating my layers), so the weight is a floating point less than 1, and any float less than 1 is converted to 0 by Python's int method.

We could use the math library and specifically the math.ceil method, although since weights need to add up to 100 that could pose a different problem, and would break with trait folders with more than 100 items.

Perhaps the simplest fix here is just to specify that trait folders cannot hold more than 100 items, and keep the new int conversion, although, thinking about it, if the weights must add up to 100 and the int method rounds down, then this will fail on quite a lot of folder sizes, unless I'm missing something.

@crush0x
Copy link
Author

crush0x commented Oct 4, 2023

Here's updated code that will handle trait folders of more than 100, while also ensuring that the weight folder will always equal 100. This ensures that no matter what the trait folder size is, the outputted weights will always be an array of integers.

It does this by calculating the floating points first (before rounding), keeping track of the sum of the rounded weights (and the difference from 100), and then distributing back the difference, so the final weight adds up to 100.

This will still of course cause any trait folder that contains more than 100 items to leave some weights as equal to zero (but avoids the ZeroDivisionError). If this or something like this works for you, I'd also add a caveat in the README / docs that trait folders shouldn't contain more than 100 items.

import json
import os

from src.utils.io import list_full_dir, list_name
from src.utils.logger import get_logger


def generate_config(trait_dir: str, output: str, verbose: int) -> None:
    logger = get_logger(verbose)
    layerlist = list_name(f"{trait_dir}/*")
    path_list = list_full_dir(f"{trait_dir}/")
    item_list = [list_name(items + "/*") for items in path_list]

    weightlist = [] * len(layerlist)

    for i in range(len(item_list)):
        float_weights = [(100 / len(item_list[i])) for _ in range(len(item_list[i]))]
        int_weights = [int(w) for w in float_weights]
        
        # Find the difference between the sum of int_weights and 100
        diff = 100 - sum(int_weights)
        
        # Distribute the difference back to make the sum of weights 100
        for j in range(diff):
            int_weights[j] += 1

        weightlist.append(int_weights)
        
        for j in range(len(item_list[i])):
            item_list[i][j] = item_list[i][j].split(".")[0]

    # generate json blob
    finalized_layers = []
    for x in range(len(layerlist)):
        layer = {
            "name": layerlist[x],
            "values": item_list[x],
            "trait_path": path_list[x],
            "filename": item_list[x],
            "weights": weightlist[x],
        }
        finalized_layers.append(layer)

    config = {
        "layers": finalized_layers,
        "incompatibilities": [],
        "baseURI": "TODO",
        "name": "TODO",
        "description": "TODO",
    }

    # ensure the directory exists for the output file
    try:
        os.makedirs(os.path.dirname(output), exist_ok=True)
    except OSError:
        pass

    with open(output, "w") as outfile:
        json.dump(config, outfile, indent=4)

    logger.info(f"Generated config file at {output}")
    logger.warning(
        "You'll need to manually update the baseURI, name, and description fields."
    )

@faea726
Copy link
Contributor

faea726 commented Oct 5, 2023

Here's updated code that will handle trait folders of more than 100, while also ensuring that the weight folder will always equal 100. This ensures that no matter what the trait folder size is, the outputted weights will always be an array of integers.

It does this by calculating the floating points first (before rounding), keeping track of the sum of the rounded weights (and the difference from 100), and then distributing back the difference, so the final weight adds up to 100.

If this is more than 100 traits, int number will not qualified anymore. You should try to improve the generate function instead of build_config.

@crush0x
Copy link
Author

crush0x commented Oct 5, 2023

@faea726 yeah I mean I initially just wanted to use an open-source NFT generator rather than write one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants