{"id":1458,"date":"2021-05-13T12:46:48","date_gmt":"2021-05-13T10:46:48","guid":{"rendered":"https:\/\/craftcoders.app\/?p=1458"},"modified":"2024-08-14T14:27:51","modified_gmt":"2024-08-14T12:27:51","slug":"solving-the-knapsack-problem-with-the-jenetics-library","status":"publish","type":"post","link":"https:\/\/craftcoders.app\/solving-the-knapsack-problem-with-the-jenetics-library\/","title":{"rendered":"Solving the Knapsack Problem with the Jenetics Library"},"content":{"rendered":"\r\n

According to its official documents<\/a>, Jenetics is a library that is used for programming evolutionary algorithms written in Java. Jenetics is implemented using the Java Stream<\/em> interface, so it works smoothly with the rest of the Java Stream<\/em> API. Evolutionary algorithms have their roots in biology, as they use mechanisms inspired by biological evolution, such as reproduction, mutation, recombination, and selection. If you want to learn more about the theory behind evolutionary algorithms, I’d suggest reading Introduction to Evolutionary Algorithms<\/a> first.<\/p>\r\n\r\n\r\n\r\n

Disclaimer:<\/strong> This blog post is based on Introduction to Jenetics Library<\/a> from Baeldung. But it is using the current library version (6.2.0) and a more complex example: The knapsack problem without using the libraries provided classes for the problem.<\/p>\r\n\r\n\r\n\r\n

The knapsack problem<\/h2>\r\n\r\n\r\n\r\n\r\n\r\n
\r\n

\"\"Given a set of items, each with a weight and a value, determine the number of each item to include in a collection so that the total weight is less than or equal to a given limit and the total value is as large as possible. It derives its name from the problem faced by someone who is constrained by a fixed-size knapsack<\/a> and must fill it with the most valuable items.<\/p>\r\nWikipedia<\/a><\/cite><\/blockquote>\r\n\r\n\r\n\r\n\r\n\r\n

Defining the problem in code<\/h2>\r\n\r\n\r\n\r\n

In the following example, we have a class called “Knapsack” that represents our problem. The class defines items that consist of a size and a value (possibleKnapsackItems<\/strong>). These items are initialized with random values between 0 and 10 and put in a list to represent the items we can put into our knapsack. Furthermore, the class defines the maximum size the knapsack can hold. Attention: Don’t mix up the size of the knapsack (Knapsack.getKnapsackSize<\/strong>) with the number of items that we could<\/em> put in the knapsack (Knapsack.getItemCount<\/strong>). The items that we actually put into the knapsack will be defined later in our evolutionary algorithm.<\/p>\r\n\r\n\r\n\r\n

public final class <\/strong>Knapsack {\r\n    private final <\/strong>List<Item> possibleKnapsackItems<\/strong>; \/\/ items that *might* end up in the knapsack, depending on chromosome\r\n    <\/em>private int knapsackSize<\/strong>;\r\n\r\n    public <\/strong>Knapsack(List<Item> possibleItems, int <\/strong>knapsackSize) {\r\n        this<\/strong>.possibleKnapsackItems <\/strong>= possibleItems;\r\n        this<\/strong>.knapsackSize <\/strong>= knapsackSize;\r\n    }\r\n\r\n    public static <\/strong>Knapsack initializeWithRandomItems(int <\/strong>size, int <\/strong>knapsackSize) {\r\n        Random random = new <\/strong>Random(123);\r\n        List<Item> items = Stream.generate<\/em>(() -> \r\n                new <\/strong>Item((int<\/strong>) (random.nextDouble()*10),(int<\/strong>) (random.nextDouble()*10)))\r\n                .limit(size)\r\n                .collect(Collectors.toList<\/em>());\r\n        return new <\/strong>Knapsack(items, knapsackSize);\r\n    }\r\n\r\n    public <\/strong>Item getItemByIndex(int <\/strong>index) { return this<\/strong>.possibleKnapsackItems<\/strong>.get(index); }\r\n    public int <\/strong>getItemCount() { return this<\/strong>.possibleKnapsackItems<\/strong>.size(); }\r\n    public int <\/strong>getKnapsackSize() { return this<\/strong>.knapsackSize<\/strong>; }\r\n\r\n    public static final class <\/strong>Item {\r\n        private final int size<\/strong>;\r\n        private final int value<\/strong>;\r\n\r\n        public <\/strong>Item(final int <\/strong>size, final int <\/strong>value) {\r\n            this<\/strong>.size <\/strong>= Requires.nonNegative<\/em>(size);\r\n            this<\/strong>.value <\/strong>= Requires.nonNegative<\/em>(value);\r\n        }\r\n\r\n        public int <\/strong>getSize() { return size<\/strong>; }\r\n        public int <\/strong>getValue() { return value<\/strong>; }\r\n    }\r\n}<\/code><\/pre>\r\n\r\n\r\n\r\n\r\n\r\n

Let’s get started with the Jenetics Library<\/h2>\r\n\r\n\r\n\r\n

In order to use Jenetics, we need to add the following dependency into our build.gradle<\/em>:<\/p>\r\n\r\n\r\n\r\n

implementation 'io.jenetics:jenetics:6.2.0'<\/strong><\/code><\/pre>\r\n\r\n\r\n\r\n

Next we create a runnable class App<\/strong> that will use the Jenetics library and our Knapsack class to run a genetic algorithm. First, let’s make use of our previously created class: We create a knapsack with a size of 100 and 80 items from which we can pick.<\/p>\r\n\r\n\r\n\r\n

public class <\/strong>App {\r\n    private final static int ITEM_COUNT <\/em><\/strong>= 80;\r\n    private final static int KNAPSACK_SIZE <\/em><\/strong>= 100;\r\n    private final static int POPULATION_SIZE <\/em><\/strong>= 500;\r\n\r\n    private final<\/strong> Knapsack knapsack <\/strong>= Knapsack.initializeWithRandomItems<\/em>(ITEM_COUNT<\/em><\/strong>, KNAPSACK_SIZE<\/em><\/strong>);\r\n\r\n    public static void <\/strong>main(String[] args) {\r\n        new <\/strong>App().run(POPULATION_SIZE<\/em><\/strong>);\r\n    }\r\n\r\n    public void <\/strong>run(int <\/strong>populationSize) {\r\n        \/\/ TODO Run the genetic algorithm\r\n    }\r\n}<\/code><\/pre>\r\n\r\n\r\n\r\n

Let’s work on the run()<\/strong> function. We need to convert the Knapsack problem into another representation that a genetic algorithm can work with, namely a chromosome. And indeed we can transform it into a so-called binary problem, where each one represents an item we put into the knapsack, each zero represents an item we don’t put in the knapsack.<\/p>\r\n\r\n\r\n\r\n

\r\n
\"\"\r\n
Source: Towards Data Science<\/a><\/figcaption>\r\n<\/figure>\r\n<\/div>\r\n\r\n\r\n\r\n

Using the Jenetics library we can create a BitChromosome<\/em> with a length of 80 which is equal to the number of items we can choose from (ITEM_COUNT<\/strong>) and a probability of having 1’s in the chromosome equal to 0.3. These BitChromosomes are accessible via a factory, meaning we can generate as many randomly initialized chromosomes as we want our population size to be.<\/p>\r\n\r\n\r\n\r\n

final<\/strong> Factory<Genotype<BitGene>> gtf =\r\n        Genotype.of<\/em>(BitChromosome.of<\/em>(this<\/strong>.knapsack<\/strong>.getItemCount(), 0.3));<\/code><\/pre>\r\n\r\n\r\n\r\n

Now, let’s create the execution environment:<\/p>\r\n\r\n\r\n\r\n

final<\/strong> Engine<BitGene, Integer> engine = Engine\r\n        .builder<\/em>(this<\/strong>::fitness, gtf)\r\n        .populationSize(populationSize)\r\n        .build();<\/code><\/pre>\r\n\r\n\r\n\r\n

The Engine will run our genetic algorithm and needs a couple of information:<\/p>\r\n\r\n\r\n\r\n

    \r\n
  1. The factory we just created, that produces our random chromosomes<\/li>\r\n
  2. The number of random chromosomes we want to create and compare (called populationSize<\/strong>)<\/li>\r\n
  3. Last but not least, a fitness function which we didn’t define, yet<\/li>\r\n<\/ol>\r\n\r\n\r\n\r\n

    The Fitness Function<\/h3>\r\n\r\n\r\n\r\n

    The fitness function calculates the fitness of each chromosome. In the case of the knapsack problem, the fitness is equal to the sum of the values of the individual elements that we place in our knapsack (i.e. items with corresponding one in the chromosome). How to put that into code, is something you can think about now \ud83d\ude09<\/p>\r\n\r\n\r\n\r\n

    private <\/strong>Integer fitness(Genotype<BitGene> gt) {\r\n    BitChromosome chromosome = gt.chromosome().as(BitChromosome.class<\/strong>);\r\n    int fitness = 0;\r\n    \/\/ TODO: Calculate fitness\r\n    return fitness;<\/em>\r\n}<\/code><\/pre>\r\n\r\n\r\n\r\n

    A first run<\/h3>\r\n\r\n\r\n\r\n

    In the final step, in our run function, we add some basic statistics, start the evolution and collect the results:<\/p>\r\n\r\n\r\n\r\n

    final <\/strong>EvolutionStatistics<Integer, ?> statistics = EvolutionStatistics.ofNumber<\/em>();\r\nfinal <\/strong>Phenotype<BitGene, Integer> best = engine.stream()\r\n        \/\/ Truncate the evolution stream after 7 \"steady\"\r\n        \/\/ generations.\r\n        <\/em>.limit(bySteadyFitness<\/em>(10))\r\n        \/\/ Update the evaluation statistics after\r\n        \/\/ each generation\r\n        <\/em>.peek(statistics)\r\n        \/\/ Collect (reduce) the evolution stream to\r\n        \/\/ its best phenotype.\r\n        <\/em>.collect(toBestPhenotype<\/em>());\r\n\r\nSystem.out<\/em><\/strong>.println(statistics);\r\nSystem.out<\/em><\/strong>.println(best);<\/code><\/pre>\r\n\r\n\r\n\r\n

    If you put everything together and implemented the fitness function correctly, you should end up with a result looking like this:<\/p>\r\n\r\n\r\n\r\n

    +---------------------------------------------------------------------------+\r\n |  Time statistics                                                          |\r\n +---------------------------------------------------------------------------+\r\n |             Selection: sum=0,029213700000 s; mean=0,000811491667 s        |\r\n |              Altering: sum=0,120244900000 s; mean=0,003340136111 s        |\r\n |   Fitness calculation: sum=0,054355500000 s; mean=0,001509875000 s        |\r\n |     Overall execution: sum=0,199033900000 s; mean=0,005528719444 s        |\r\n +---------------------------------------------------------------------------+\r\n |  Evolution statistics                                                     |\r\n +---------------------------------------------------------------------------+\r\n |           Generations: 36                                                 |\r\n |               Altered: sum=133.010; mean=3694,722222222                   |\r\n |                Killed: sum=0; mean=0,000000000                            |\r\n |              Invalids: sum=0; mean=0,000000000                            |\r\n +---------------------------------------------------------------------------+\r\n |  Population statistics                                                    |\r\n +---------------------------------------------------------------------------+\r\n |                   Age: max=14; mean=2,183056; var=7,349621                |\r\n |               Fitness:                                                    |\r\n |                      min  = 0,000000000000                                |\r\n |                      max  = 188,000000000000                              |\r\n |                      mean = 134,464166666667                              |\r\n |                      var  = 4503,017550280571                             |\r\n |                      std  = 67,104527047589                               |\r\n +---------------------------------------------------------------------------+\r\n [11101010|00000100|11000101|10001000|10001111|10100000|01010010|10110000|11000101|10000101] -> 188<\/code><\/pre>\r\n\r\n\r\n\r\n

    If so, congratulations! You made it.<\/p>\r\n\r\n\r\n\r\n

    Further Optimiziation<\/h3>\r\n\r\n\r\n\r\n

    So up until now, we told the engine to learn using 500 generations and let it decide on itself how to do mutation, recombination, and selection. Of course, if you want to improve the quality of your best phenotype you can configure these things yourself. An easy thing to do is to increase the number of generations to i.e. 5000 and your results will probably improve. But you can also tweak several things like mutation yourself:<\/p>\r\n\r\n\r\n\r\n

    final <\/strong>Engine<BitGene, Integer> engine = Engine\r\n        .builder<\/em>(this<\/strong>::fitness, gtf)\r\n        .populationSize(populationSize)\r\n        .survivorsSelector(new TournamentSelector<>(5))                    \r\n        .offspringSelector(new RouletteWheelSelector<>())                   \r\n        .alterers(\r\n            new Mutator<>(0.115),\r\n            new SinglePointCrossover<>(0.16))\r\n        .build();<\/code><\/pre>\r\n\r\n\r\n\r\n

    But to gain some real improvements using your own configuration is something that is pretty time consuming and would need another blogpost, so I’ll leave that to you \ud83d\ude00<\/p>\r\n\r\n\r\n\r\n

    Greetings,<\/p>\r\n\r\n\r\n\r\n

    Domi<\/p>\r\n","protected":false},"excerpt":{"rendered":"

    According to its official documents, Jenetics is a library that is used for programming evolutionary algorithms written in Java. Jenetics is implemented using the Java Stream interface, so it works smoothly with the rest of the Java Stream API. Evolutionary algorithms have their roots in biology, as they use mechanisms inspired by biological evolution, such as reproduction, mutation, recombination, and … Read More<\/a><\/p>\n","protected":false},"author":3,"featured_media":2254,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[108,112],"tags":[],"acf":[],"yoast_head":"\nSolving the Knapsack Problem with the Jenetics Library - CraftCoders.app<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/craftcoders.app\/solving-the-knapsack-problem-with-the-jenetics-library\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Solving the Knapsack Problem with the Jenetics Library - CraftCoders.app\" \/>\n<meta property=\"og:description\" content=\"According to its official documents, Jenetics is a library that is used for programming evolutionary algorithms written in Java. Jenetics is implemented using the Java Stream interface, so it works smoothly with the rest of the Java Stream API. Evolutionary algorithms have their roots in biology, as they use mechanisms inspired by biological evolution, such as reproduction, mutation, recombination, and ... Read More\" \/>\n<meta property=\"og:url\" content=\"https:\/\/craftcoders.app\/solving-the-knapsack-problem-with-the-jenetics-library\/\" \/>\n<meta property=\"og:site_name\" content=\"CraftCoders.app\" \/>\n<meta property=\"article:published_time\" content=\"2021-05-13T10:46:48+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-08-14T12:27:51+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/craftcoders.app\/wp-content\/uploads\/2021\/05\/JENETICS-1-850x710-1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"850\" \/>\n\t<meta property=\"og:image:height\" content=\"710\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Dominik J\u00fclg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Dominik J\u00fclg\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/craftcoders.app\/solving-the-knapsack-problem-with-the-jenetics-library\/\",\"url\":\"https:\/\/craftcoders.app\/solving-the-knapsack-problem-with-the-jenetics-library\/\",\"name\":\"Solving the Knapsack Problem with the Jenetics Library - CraftCoders.app\",\"isPartOf\":{\"@id\":\"https:\/\/craftcoders.app\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/craftcoders.app\/solving-the-knapsack-problem-with-the-jenetics-library\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/craftcoders.app\/solving-the-knapsack-problem-with-the-jenetics-library\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/craftcoders.app\/wp-content\/uploads\/2021\/05\/JENETICS-1-850x710-1.png\",\"datePublished\":\"2021-05-13T10:46:48+00:00\",\"dateModified\":\"2024-08-14T12:27:51+00:00\",\"author\":{\"@id\":\"https:\/\/craftcoders.app\/#\/schema\/person\/950725b140a7ce1147b1308a746a8cce\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/craftcoders.app\/solving-the-knapsack-problem-with-the-jenetics-library\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/craftcoders.app\/solving-the-knapsack-problem-with-the-jenetics-library\/#primaryimage\",\"url\":\"https:\/\/craftcoders.app\/wp-content\/uploads\/2021\/05\/JENETICS-1-850x710-1.png\",\"contentUrl\":\"https:\/\/craftcoders.app\/wp-content\/uploads\/2021\/05\/JENETICS-1-850x710-1.png\",\"width\":850,\"height\":710},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/craftcoders.app\/#website\",\"url\":\"https:\/\/craftcoders.app\/\",\"name\":\"CraftCoders.app\",\"description\":\"Jira and Confluence apps\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/craftcoders.app\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/craftcoders.app\/#\/schema\/person\/950725b140a7ce1147b1308a746a8cce\",\"name\":\"Dominik J\u00fclg\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/craftcoders.app\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/bd18a95cdfc659ad254c8a3bd7f70cc5?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/bd18a95cdfc659ad254c8a3bd7f70cc5?s=96&d=mm&r=g\",\"caption\":\"Dominik J\u00fclg\"},\"url\":\"https:\/\/craftcoders.app\/author\/domi\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Solving the Knapsack Problem with the Jenetics Library - CraftCoders.app","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/craftcoders.app\/solving-the-knapsack-problem-with-the-jenetics-library\/","og_locale":"en_US","og_type":"article","og_title":"Solving the Knapsack Problem with the Jenetics Library - CraftCoders.app","og_description":"According to its official documents, Jenetics is a library that is used for programming evolutionary algorithms written in Java. Jenetics is implemented using the Java Stream interface, so it works smoothly with the rest of the Java Stream API. Evolutionary algorithms have their roots in biology, as they use mechanisms inspired by biological evolution, such as reproduction, mutation, recombination, and ... Read More","og_url":"https:\/\/craftcoders.app\/solving-the-knapsack-problem-with-the-jenetics-library\/","og_site_name":"CraftCoders.app","article_published_time":"2021-05-13T10:46:48+00:00","article_modified_time":"2024-08-14T12:27:51+00:00","og_image":[{"width":850,"height":710,"url":"https:\/\/craftcoders.app\/wp-content\/uploads\/2021\/05\/JENETICS-1-850x710-1.png","type":"image\/png"}],"author":"Dominik J\u00fclg","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Dominik J\u00fclg","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/craftcoders.app\/solving-the-knapsack-problem-with-the-jenetics-library\/","url":"https:\/\/craftcoders.app\/solving-the-knapsack-problem-with-the-jenetics-library\/","name":"Solving the Knapsack Problem with the Jenetics Library - CraftCoders.app","isPartOf":{"@id":"https:\/\/craftcoders.app\/#website"},"primaryImageOfPage":{"@id":"https:\/\/craftcoders.app\/solving-the-knapsack-problem-with-the-jenetics-library\/#primaryimage"},"image":{"@id":"https:\/\/craftcoders.app\/solving-the-knapsack-problem-with-the-jenetics-library\/#primaryimage"},"thumbnailUrl":"https:\/\/craftcoders.app\/wp-content\/uploads\/2021\/05\/JENETICS-1-850x710-1.png","datePublished":"2021-05-13T10:46:48+00:00","dateModified":"2024-08-14T12:27:51+00:00","author":{"@id":"https:\/\/craftcoders.app\/#\/schema\/person\/950725b140a7ce1147b1308a746a8cce"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/craftcoders.app\/solving-the-knapsack-problem-with-the-jenetics-library\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/craftcoders.app\/solving-the-knapsack-problem-with-the-jenetics-library\/#primaryimage","url":"https:\/\/craftcoders.app\/wp-content\/uploads\/2021\/05\/JENETICS-1-850x710-1.png","contentUrl":"https:\/\/craftcoders.app\/wp-content\/uploads\/2021\/05\/JENETICS-1-850x710-1.png","width":850,"height":710},{"@type":"WebSite","@id":"https:\/\/craftcoders.app\/#website","url":"https:\/\/craftcoders.app\/","name":"CraftCoders.app","description":"Jira and Confluence apps","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/craftcoders.app\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/craftcoders.app\/#\/schema\/person\/950725b140a7ce1147b1308a746a8cce","name":"Dominik J\u00fclg","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/craftcoders.app\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/bd18a95cdfc659ad254c8a3bd7f70cc5?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/bd18a95cdfc659ad254c8a3bd7f70cc5?s=96&d=mm&r=g","caption":"Dominik J\u00fclg"},"url":"https:\/\/craftcoders.app\/author\/domi\/"}]}},"_links":{"self":[{"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/posts\/1458"}],"collection":[{"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/comments?post=1458"}],"version-history":[{"count":6,"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/posts\/1458\/revisions"}],"predecessor-version":[{"id":2284,"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/posts\/1458\/revisions\/2284"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/media\/2254"}],"wp:attachment":[{"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/media?parent=1458"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/categories?post=1458"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/craftcoders.app\/wp-json\/wp\/v2\/tags?post=1458"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}